{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Prerequisites:\n",
    "#!pip install requests\n",
    "#!pip install python-dotenv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1. Login\n",
    "from api import BigApi\n",
    "\n",
    "BigApi.init(\".admin.env\")\n",
    "BigApi.adminLogin()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Enumerate BigOrderAction (not needed)\n",
    "from enum import Enum\n",
    "\n",
    "schemas = BigApi.adminGet(f\"/admin/shop/schemas\")\n",
    "\n",
    "# print(schemas['BigOrderAction'])\n",
    "BigOrderAction = Enum(\"BigOrderAction\", dict([(v, k) for k, v in schemas[\"BigOrderAction\"].items()]))\n",
    "schemas['BigOrderAction']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# (Basic API Tests)\n",
    "data = BigApi.adminGet(f\"/admin/shop/shopify_orders?status=any&search=%23BS01183823\")\n",
    "\n",
    "data['orders'][0]['bigOrder']['ipd']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# (CSV Tests)\n",
    "import pandas as pd\n",
    "import datetime\n",
    "\n",
    "data = pd.read_csv(\"C:/Users/decid/Documents/Bigscreen/LENS_ORDERS/rxLensFulfillment.csv\")\n",
    "# data = pd.read_csv(\"C:/Users/decid/Documents/Bigscreen/LENS_ORDERS/rxLensFulfillment - Copy.csv\")\n",
    "# data = pd.DataFrame(columns = ['soup', 'nuts'])\n",
    "\n",
    "# assign a new row of values to end of dataframe\n",
    "# data.loc[len(data.index)] = [None, \"Test\", \"Test\", \"Test\", \"Test\", \"Test\"]\n",
    "# data = pd.concat([data, data.loc[data['ID'] == '#BS01166323']])\n",
    "\n",
    "# drop the first row\n",
    "# data.drop(data.loc[data['ID'] == '#BS01114323'].index, inplace=True)\n",
    "\n",
    "# soup = data.loc[data['ID'] == '#BS01247923']\n",
    "# porridge = data.loc[data.index == 15]\n",
    "# soup = pd.concat([soup, porridge], ignore_index=True)\n",
    "# today = date.today().strftime(\"%Y-%m-%d\")\n",
    "# testDir = f\"C:/Users/decid/Documents/Bigscreen/LENS_ORDERS/test{today}.csv\"\n",
    "# len(soup.index)\n",
    "# soup.to_csv(testDir, index=False)\n",
    "# print(f\"Wrote file {testDir}\")\n",
    "\n",
    "# test = data.loc[data['ID'] == '#BS01247923', 'Vision Rx - OS'].values[0]\n",
    "# test\n",
    "\n",
    "# data.loc[data['ID'] == '#BS01247923', 'Vision Rx - OD'] = 'Test'\n",
    "\n",
    "# replace row where ID == '#BS01247923' with row 3\n",
    "#\n",
    "\n",
    "# def replaceRowWhereId(df, id, row):\n",
    "#     div = df.loc[df['ID'] == id].index[0]\n",
    "#     start = df.iloc[0:div]\n",
    "#     end = df.iloc[div+1:]\n",
    "#     return pd.concat([start, row, end], ignore_index=True)\n",
    "\n",
    "# test = replaceRowWhereId(data, '#BS01166323', data.loc[data.index == 0])\n",
    "# div = data.loc[data['ID'] == '#BS01166323'].index[0]\n",
    "# start = data.iloc[0:div]\n",
    "# end = data.iloc[div+1:]\n",
    "# half1 = data.loc[start:div]\n",
    "# test\n",
    "\n",
    "# data.loc[data['ID'] == '#BS01114323', 'Order Date'] = 'DATEDATEDATE'\n",
    "# test = data.get(data['ID'] == '#BS01265223')\n",
    "\n",
    "# orderDate = \"2023-02-14T14:14:46-05:00\"\n",
    "# convert orderDate to string in format YYYY-MM-DD_HH-MM-SS in PST\n",
    "# orderDate = datetime.datetime.strptime(orderDate, \"%Y-%m-%dT%H:%M:%S%z\")\n",
    "# orderDate = orderDate.astimezone(datetime.timezone(datetime.timedelta(hours=-8)))\n",
    "# orderDate = orderDate.strftime(\"%Y-%m-%d_%H:%M:%S\")\n",
    "# orderDate\n",
    "\n",
    "soup = data.loc[data['Order Date'].isnull().values]\n",
    "for csvOrderId in soup['ID']:\n",
    "  print(csvOrderId)\n",
    "\n",
    "# display(data)\n",
    "# display(data.loc[data['ID'] == '#BS01247923'])\n",
    "# display(data.loc[0])\n",
    "# display(data.loc[0]['ID'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# (JSON Tests)\n",
    "import json\n",
    "import os\n",
    "\n",
    "outputDir = 'C:/Users/decid/Documents/Bigscreen/LENS_ORDERS'\n",
    "\n",
    "# function that accepts a list of properties formatted {name: 'name', value: 'value'} and accepts a name and returns the value\n",
    "def getSiblingProp(props, findVal, findKey = 'name', returnKey = 'value'):\n",
    "  for prop in props:\n",
    "    if(prop[findKey] == findVal):\n",
    "      return prop[returnKey]\n",
    "  return None\n",
    "\n",
    "# function that accepts a key and value, and returns the index of the first object in an array of objects that has that key/value pair\n",
    "def getIndexByProp(props, propKey, matchVal):\n",
    "  for i in range(len(props)):\n",
    "    if(props[i][propKey] == matchVal):\n",
    "      return i\n",
    "  return None\n",
    "\n",
    "\n",
    "beyondOrdersFile = f'{outputDir}/beyondOrders.json'\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(beyondOrdersFile):\n",
    "    try:\n",
    "        with open(beyondOrdersFile, 'r') as file:\n",
    "            beyondOrdersByEmail = json.load(file)\n",
    "        # print('Original data:', orderLib) #debug\n",
    "    except json.JSONDecodeError:\n",
    "        assert False, f\"Error: {beyondOrdersFile} contains invalid JSON. Please check formatting.\"\n",
    "else:\n",
    "    print(f\"{beyondOrdersFile} does not exist. Populate and write it using the cell above.\")\n",
    "\n",
    "\n",
    "\n",
    "beyondOrderId = getSiblingProp(beyondOrdersByEmail, 'seth.persigehl@unity3d.com', 'email', 'orderName')\n",
    "if(beyondOrderId != None):\n",
    "    print(f\"Found Beyond order: {beyondOrderId}\")\n",
    "    dateSourceOrder = beyondOrdersByEmail[getIndexByProp(beyondOrdersByEmail, 'orderName', beyondOrderId)]\n",
    "else:\n",
    "    print(\"No Beyond order found for this email\")\n",
    "\n",
    "display(dateSourceOrder['shopifyOrder'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2. Set up essential shared variables\n",
    "### default state ###\n",
    "PROCESS_ALL_ORDERS = True\n",
    "PRETEND_NOT_FORCED = False\n",
    "PREVENT_BACKUPS = False\n",
    "SHOW_DEBUG = False\n",
    "### debugging only state ###\n",
    "# PROCESS_ALL_ORDERS = False #debug\n",
    "# PRETEND_NOT_FORCED = True #debug\n",
    "# PREVENT_BACKUPS = True #debug\n",
    "# SHOW_DEBUG = True #debug\n",
    "\n",
    "\n",
    "lensOrdersFile = 'C:/Users/decid/Documents/Bigscreen/lensOrders.json'\n",
    "outputDir = 'C:/Users/decid/Documents/Bigscreen/LENS_ORDERS'\n",
    "beyondOrdersFile = f'{outputDir}/beyondOrders.json'\n",
    "rxLensMasterCsv = f'{outputDir}/rxOrdersOutstanding.csv'\n",
    "rxLensPlacedCsv = f'{outputDir}/rxOrdersPlaced.csv'\n",
    "rxLensShippedCsv = f'{outputDir}/rxOrdersShipped.csv'\n",
    "\n",
    "\n",
    "excludedOrderIds = [\n",
    "  # '#BS01114323', # note about it\n",
    "]\n",
    "excludedOrderTags = [\n",
    "  'team-bigscreen',\n",
    "  # 'competitor',\n",
    "]\n",
    "forcedOrderIds = [\n",
    "  # '#BS01285723',\n",
    "  # '#BS03742947',\n",
    "  # '#BS03744337',\n",
    "  # '#BS03744697',\n",
    "  # '#BS02938652', # not a duplicate, unlike the above\n",
    "  # '#BS02939342', # not present on the original rxLensFulfillment.csv\n",
    "  # '#BS01109823', # a fulfilled order\n",
    "  # ^^^ test forced orders ^^^\n",
    "  # '#BS01116423', # a fulfilled order that stayed for some reason\n",
    "  # '#BS01125023', # a canceled order that stayed for some reason\n",
    "  # '#BS01127623', # empty order date\n",
    "  # '#BS01129623', # empty order date\n",
    "  # '#BS03754067', # LUCY gets default order date\n",
    "  # ^^^ debugging forced orders ^^^\n",
    "  # vvv orders missing from last prepick vvv\n",
    "  # \"#BS03754757\",\n",
    "  # \"#BS03747467\",\n",
    "  # \"#BS03960233\",\n",
    "  # \"#BS03960223\",\n",
    "  # \"#BS03742767\",\n",
    "  # vvv priority orders vvv\n",
    "  \"#BS02934092\",\n",
    "  \"#BS03967043\",\n",
    "  \"#BS03740057\",\n",
    "  \"#BS02936652\",\n",
    "]\n",
    "forcedOrderTags = [\n",
    "  # 'vrchat',\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# [RUN DAILY] Recompile counts of Beyond orders by email -> save as JSON file\n",
    "import json\n",
    "# import os\n",
    "import time\n",
    "\n",
    "def orderExcludesTags(orderDict, tagsArr):\n",
    "  for tag in tagsArr:\n",
    "    if(tag in orderDict['shopifyOrder']['tags']):\n",
    "      return False\n",
    "  return True\n",
    "\n",
    "def orderValid(orderDict):\n",
    "  # return orderDict['shopifyOrder']['cancel_reason'] == None and orderDict['shopifyOrder']['fulfillment_status'] == None\n",
    "  # return orderDict['shopifyOrder']['cancel_reason'] == None\n",
    "  return True\n",
    "\n",
    "def isLateEnough(orderDict):\n",
    "  earliestDate = \"2023-02-13T00:06:00-08:00\"\n",
    "  return orderDict['shopifyOrder']['created_at'] > earliestDate\n",
    "\n",
    "# function that accepts a list of properties formatted {name: 'name', value: 'value'} and accepts a name and returns the value\n",
    "def getSiblingProp(props, findVal, findKey = 'name', returnKey = 'value'):\n",
    "  for prop in props:\n",
    "    if(prop[findKey] == findVal):\n",
    "      return prop[returnKey]\n",
    "  return None\n",
    "\n",
    "# function that accepts a key and value, and returns the index of the first object in an array of objects that has that key/value pair\n",
    "def getIndexByProp(props, propKey, matchVal):\n",
    "  for i in range(len(props)):\n",
    "    if(props[i][propKey] == matchVal):\n",
    "      return i\n",
    "  return None\n",
    "\n",
    "def simplifyShopifyDict(shopifyOrderDict):\n",
    "  retKeys = [\n",
    "    \"id\",\n",
    "    \"name\",\n",
    "    \"email\",\n",
    "    \"tags\",\n",
    "    \"created_at\",\n",
    "    \"fulfillment_status\",\n",
    "    \"cancel_reason\",\n",
    "    \"billing_address\",\n",
    "    \"line_items\",\n",
    "  ]\n",
    "  return {k: shopifyOrderDict.get(k, \"\") for k in retKeys}\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "excludedTags = ['team-bigscreen']\n",
    "beyondOrdersByEmail = [] #item format {email: '', orderName: ''}\n",
    "ordersProcessed = 0\n",
    "skipCount = 0\n",
    "cursor = ''\n",
    "debugOut = ''\n",
    "\n",
    "print(f\"START: Compiling array of all Beyond orders per email address\")\n",
    "\n",
    "while True:\n",
    "  data = BigApi.adminGet(f\"/admin/shop/shopify_orders/?status=any&limit=250\" + ('' if cursor == '' else '&cursor=' + cursor))\n",
    "  orders = data['orders']\n",
    "\n",
    "  for order in orders:\n",
    "    # debugOut = order #debug\n",
    "    # break #debug\n",
    "    if not isLateEnough(order):\n",
    "      continue\n",
    "    ordersProcessed += 1\n",
    "    if(orderValid(order) and orderExcludesTags(order, excludedTags)):\n",
    "      items = order['shopifyOrder']['line_items']\n",
    "      for item in items:\n",
    "        if(item['name'] == 'Bigscreen Beyond - Measure Me'):\n",
    "          existingIndex = getIndexByProp(beyondOrdersByEmail, 'email', order['shopifyOrder']['email'])\n",
    "          if(existingIndex != None):\n",
    "            beyondOrdersByEmail[existingIndex]['beyondOrdersQty'] += 1\n",
    "          else:\n",
    "            shopifyOrder = simplifyShopifyDict(order['shopifyOrder'])\n",
    "            beyondOrdersByEmail.append({\n",
    "              'email': order['shopifyOrder']['email'],\n",
    "              'orderName': order['shopifyOrder']['name'],\n",
    "              'beyondOrdersQty': 1,\n",
    "              'bigOrderId': order['bigOrder']['id'] if('bigOrder' in order) else '',\n",
    "              'shopifyOrder': shopifyOrder,\n",
    "            })\n",
    "    else:\n",
    "      skipCount += 1\n",
    "    if(ordersProcessed % 100 == 0):\n",
    "      print(f\"{ordersProcessed} total orders processed...\")\n",
    "    if(ordersProcessed % 500 == 0):\n",
    "      # five second time delay to avoid rate limiting\n",
    "      time.sleep(5)\n",
    "      BigApi.adminLogin()\n",
    "\n",
    "  # break if no more nextCursor\n",
    "  if('nextCursor' not in data):\n",
    "    print(f\"### Finished iterating through {ordersProcessed} total orders ###\")\n",
    "    print(f\"There are a grand total of {len(beyondOrdersByEmail)} non-canceled Beyond orders\")\n",
    "    print(f\"{skipCount} orders have been skipped (canceled or excluded)\")\n",
    "    break\n",
    "  else:\n",
    "    cursor = data['nextCursor']\n",
    "\n",
    "# for row in csv:\n",
    "#   email = row[4]\n",
    "#   orderName = getBeyondOrderName(beyondOrdersByEmail, email)\n",
    "#   row.append(orderName)\n",
    "\n",
    "# Write the modified dictionary back to the JSON file\n",
    "with open(beyondOrdersFile, 'w') as file:\n",
    "    json.dump(beyondOrdersByEmail, file, indent=4)  # indent=4 for pretty printing\n",
    "\n",
    "print(f'Beyond order list written to file \"{beyondOrdersFile}\":', beyondOrdersByEmail)\n",
    "\n",
    "# debugOut #debug\n",
    "# data #debug"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3. Set up functions and amass the latest entirety of Shopify orders\n",
    "import json\n",
    "import os\n",
    "import pandas as pd\n",
    "import urllib.parse\n",
    "from datetime import date\n",
    "import datetime\n",
    "import time\n",
    "\n",
    "def orderExcludesTags(orderDict, tagsArr):\n",
    "  for tag in tagsArr:\n",
    "    if(tag in orderDict['shopifyOrder']['tags']):\n",
    "      return False\n",
    "  return True\n",
    "\n",
    "def orderIncludesTags(orderDict, tagsArr):\n",
    "  if len(tagsArr) == 0:\n",
    "    return False\n",
    "  for tag in tagsArr:\n",
    "    if(tag not in orderDict['shopifyOrder']['tags']):\n",
    "      return False\n",
    "  return True\n",
    "\n",
    "def actualIpd(ipd):\n",
    "  # seems redundant but keep it\n",
    "  ipdString = f\"{ipd}\"\n",
    "  ipdInt = int(ipdString)\n",
    "  if ipdInt == -1:\n",
    "    ipdInt = -1\n",
    "  elif ipdInt < 55:\n",
    "    ipdInt = 55\n",
    "  elif ipdInt == 70:\n",
    "    ipdInt = 69\n",
    "  elif ipdInt > 72:\n",
    "    ipdInt = 72\n",
    "  return f\"{ipdInt}\"\n",
    "\n",
    "def isLateEnough(orderDict):\n",
    "  earliestDate = \"2023-02-13T00:06:00-08:00\"\n",
    "  return orderDict['shopifyOrder']['created_at'] > earliestDate\n",
    "\n",
    "def isPartOfQ3(orderDict):\n",
    "  cutoffDate = \"2023-07-22T00:00:00-08:00\"\n",
    "  return orderDict['shopifyOrder']['created_at'] < cutoffDate\n",
    "\n",
    "def orderValidityStatus(orderDict, lensOrderDict):\n",
    "  if(orderId in excludedOrderIds):\n",
    "    return 'excludedId'\n",
    "  elif(orderIncludesTags(orderDict, excludedOrderTags)):\n",
    "    return 'excludedTag'\n",
    "  elif(not isLateEnough(orderDict)):\n",
    "    return 'orderTooEarly'\n",
    "  # elif(not isPartOfQ3(orderDict)):\n",
    "  #   return 'orderTooLate'\n",
    "  # elif(orderDict['shopifyOrder']['billing_address']['country'] != 'United States'):\n",
    "  #   return 'international'\n",
    "  elif(item['fulfillment_status'] == 'fulfilled'):\n",
    "    return 'fulfilled'\n",
    "  elif(item['fulfillable_quantity'] == 0):\n",
    "    return 'canceled'\n",
    "  return 'valid'\n",
    "\n",
    "# function that accepts a list of properties formatted {name: 'name', value: 'value'} and accepts a name and returns the value\n",
    "def getSiblingProp(props, findVal, findKey = 'name', returnKey = 'value'):\n",
    "  for prop in props:\n",
    "    if(prop[findKey] == findVal):\n",
    "      return prop[returnKey]\n",
    "  return None\n",
    "\n",
    "# function that accepts a key and value, and returns the index of the first object in an array of objects that has that key/value pair\n",
    "def getIndexByProp(props, propKey, matchVal):\n",
    "  for i in range(len(props)):\n",
    "    if(props[i][propKey] == matchVal):\n",
    "      return i\n",
    "  return None\n",
    "\n",
    "def parseRx(rx):\n",
    "  if(rx == None):\n",
    "    return ''\n",
    "  rx = rx.split(' | ')\n",
    "  if(len(rx) < 3):\n",
    "    return 'Sph 0.00, Cyl 0.00, Axis 000'\n",
    "  sph = float(rx[0])\n",
    "  cyl = float(rx[1])\n",
    "  if(sph > 3.0 or cyl > 3.0 or (sph + cyl >= 3.0)):\n",
    "    return 'SKIP'\n",
    "  rx[0] = 'Sph ' + rx[0]\n",
    "  rx[1] = 'Cyl ' + rx[1]\n",
    "  rx[2] = 'Axis ' + rx[2].replace('x', '').zfill(3)\n",
    "  return ', '.join(rx)\n",
    "\n",
    "def csvDatelessChecks(orderList): # necessary b/c /admin/shop/shopify_orders endpoint doesn't return orders that are fulfilled or canceled\n",
    "  if len(orderList) == 0:\n",
    "    dCount = 0\n",
    "    datelessOrders = rxMasterList.loc[rxMasterList['Order Date'].isnull().values]\n",
    "    if len(datelessOrders['ID']) > 0:\n",
    "      print(f\"Found {len(datelessOrders['ID'])} dateless orders. Loading now...\")\n",
    "    for orderId in datelessOrders['ID']:\n",
    "      dCount += 1\n",
    "      data = BigApi.adminGet(f\"/admin/shop/shopify_orders?status=any&search={urllib.parse.quote(orderId)}\")\n",
    "      if(len(data['orders']) > 0):\n",
    "        orderList.append(data['orders'][0])\n",
    "      if(dCount % 8 == 0):\n",
    "        print(f\"Loaded {dCount} dateless orders...\")\n",
    "        # five second time delay to avoid rate limiting\n",
    "        time.sleep(5)\n",
    "        BigApi.adminLogin()\n",
    "    print(f\"Done loading {dCount} dateless orders.\")\n",
    "    return orderList\n",
    "  return []\n",
    "\n",
    "def populateForcedOrders(orderList):\n",
    "  if len(orderList) == 0:\n",
    "    fCount = 0\n",
    "    orderList = csvDatelessChecks(orderList)\n",
    "\n",
    "    # If file exists, read it. If not, quit\n",
    "    if os.path.exists(lensOrdersFile):\n",
    "        try:\n",
    "            with open(lensOrdersFile, 'r') as file:\n",
    "                lensOrdersByEmail = json.load(file)\n",
    "                if(SHOW_DEBUG): print(f\"Library of lens orders loaded from {lensOrdersFile}.\")\n",
    "            # print('Original data:', lensOrdersByEmail) #debug\n",
    "        except json.JSONDecodeError:\n",
    "            assert False, f\"Error: {lensOrdersFile} contains invalid JSON. Please check formatting.\"\n",
    "    else:\n",
    "        print(f\"{lensOrdersFile} does not exist. Populate and write it using the cell above.\")\n",
    "\n",
    "    if len(forcedOrderIds) > 0:\n",
    "      print(f\"Found {len(forcedOrderIds)} orders specified to be forced. Loading now...\")\n",
    "    for orderId in forcedOrderIds:\n",
    "      fCount += 1\n",
    "      data = BigApi.adminGet(f\"/admin/shop/shopify_orders?status=any&search={urllib.parse.quote(orderId)}\")\n",
    "      if(len(data['orders']) > 0): # check if given order is a Beyond order\n",
    "        newOrderId = getSiblingProp(lensOrdersByEmail, data['orders'][0]['shopifyOrder']['email'], 'email', 'orderName')\n",
    "        if(newOrderId != None and newOrderId != orderId):\n",
    "          data = BigApi.adminGet(f\"/admin/shop/shopify_orders?status=any&search={urllib.parse.quote(newOrderId)}\")\n",
    "      if(len(data['orders']) > 0):\n",
    "        data['orders'][0]['force'] = True\n",
    "        orderList.append(data['orders'][0])\n",
    "      if(fCount % 8 == 0):\n",
    "        print(f\"Loaded {fCount} forced orders...\")\n",
    "        # five second time delay to avoid rate limiting\n",
    "        time.sleep(5)\n",
    "        BigApi.adminLogin()\n",
    "    print(f\"Done loading {fCount} forced orders.\")\n",
    "    return orderList\n",
    "  return []\n",
    "\n",
    "def replaceRowWhereId(df, id, row):\n",
    "    div = df.loc[df['ID'] == id].index[0]\n",
    "    start = df.iloc[0:div]\n",
    "    end = df.iloc[div+1:]\n",
    "    return pd.concat([start, row, end], ignore_index=True)\n",
    "\n",
    "\n",
    "print(f\"START: Compiling array of all Beyond orders' lens insert orders\")\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(beyondOrdersFile):\n",
    "    try:\n",
    "        with open(beyondOrdersFile, 'r') as file:\n",
    "            beyondOrdersByEmail = json.load(file)\n",
    "            if(SHOW_DEBUG): print(f\"Library of Beyond orders loaded from {beyondOrdersFile}.\")\n",
    "        # print('Original data:', beyondOrdersByEmail) #debug\n",
    "    except json.JSONDecodeError:\n",
    "        assert False, f\"Error: {beyondOrdersFile} contains invalid JSON. Please check formatting.\"\n",
    "else:\n",
    "    print(f\"{beyondOrdersFile} does not exist. Populate and write it using the cell above.\")\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(rxLensMasterCsv):\n",
    "    try:\n",
    "        rxMasterList = pd.read_csv(rxLensMasterCsv)\n",
    "        # print('Original data:', rxMasterList) #debug\n",
    "    except:\n",
    "        assert False, f\"Error: {rxLensMasterCsv} contains invalid CSV data. Please check formatting.\"\n",
    "else:\n",
    "    assert False, f\"{rxLensMasterCsv} does not exist. Please create it in said directory.\"\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(rxLensPlacedCsv):\n",
    "    try:\n",
    "        placedCsv = pd.read_csv(rxLensPlacedCsv)\n",
    "        # print('Original data:', rxMasterList) #debug\n",
    "    except:\n",
    "        assert False, f\"Error: {rxLensPlacedCsv} contains invalid CSV data. Please check formatting.\"\n",
    "else:\n",
    "    assert False, f\"{rxLensPlacedCsv} does not exist. Please create it in said directory.\"\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(rxLensShippedCsv):\n",
    "    try:\n",
    "        shippedCsv = pd.read_csv(rxLensShippedCsv)\n",
    "        # print('Original data:', rxMasterList) #debug\n",
    "    except:\n",
    "        assert False, f\"Error: {rxLensShippedCsv} contains invalid CSV data. Please check formatting.\"\n",
    "else:\n",
    "    assert False, f\"{rxLensShippedCsv} does not exist. Please create it in said directory.\"\n",
    "\n",
    "\n",
    "\n",
    "csvColumns = ['ID', 'Name', 'Vision Rx - OD', 'Vision Rx - OS', 'Email', 'Order Date', 'Status', 'Group', 'Shopify Order URL', 'Beyond Order ID', 'Record Added', 'IPD']\n",
    "canceledCsv = pd.DataFrame(columns = csvColumns)\n",
    "addedStr = date.today().strftime(\"%m/%d/%Y\")\n",
    "cursor = ''\n",
    "\n",
    "shopifyOrders = []\n",
    "\n",
    "while True:\n",
    "  if(PROCESS_ALL_ORDERS):\n",
    "    data = BigApi.adminGet(f\"/admin/shop/shopify_orders?status=any&limit=250\" + ('' if cursor == '' else '&cursor=' + cursor))\n",
    "  else: ### debugging feature\n",
    "    data = {'orders': []}\n",
    "  shopifyOrders.extend(data['orders'])\n",
    "\n",
    "  if(len(shopifyOrders) % 1000 == 0):\n",
    "      print(f\"{len(shopifyOrders)} total orders processed...\")\n",
    "    # if(ordersProcessed % 500 == 0):\n",
    "    #   # five second time delay to avoid rate limiting\n",
    "    #   time.sleep(8)\n",
    "    #   BigApi.adminLogin()\n",
    "\n",
    "  # break if no more nextCursor\n",
    "  if('nextCursor' not in data):\n",
    "    print(f\"### Finished caching {len(shopifyOrders)} total orders ###\")\n",
    "    break\n",
    "  else:\n",
    "    cursor = data['nextCursor']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 4. Create backups, compile and update list of all existing lens insert orders -> store in CSV variable\n",
    "def getOdOs(itemProps):\n",
    "  odRight = getSiblingProp(itemProps, 'OD Right')\n",
    "  osLeft = getSiblingProp(itemProps, 'OS Left')\n",
    "  if(odRight == None):\n",
    "    odRight = getSiblingProp(itemProps, 'OD (Right Eye)')\n",
    "    if(odRight == None):\n",
    "      odRight = \"N/A\"\n",
    "  if(osLeft == None):\n",
    "    osLeft = getSiblingProp(itemProps, 'OS (Left Eye)')\n",
    "    if(osLeft == None):\n",
    "      osLeft = \"N/A\"\n",
    "  odRight = parseRx(odRight)\n",
    "  osLeft = parseRx(osLeft)\n",
    "  if('Prescription Change requested' in order['shopifyOrder']['tags']):\n",
    "    odRight = 'CHECK SHOPIFY NOTES'\n",
    "    osLeft = 'CHECK SHOPIFY NOTES'\n",
    "  return [odRight, osLeft]\n",
    "\n",
    "if(not PREVENT_BACKUPS):\n",
    "  now = datetime.datetime.now().strftime(\"%Y-%m-%d_%H-%M-%S\")\n",
    "  rxMasterList.to_csv(f\"{outputDir}/BACKUP_rxOrdersOutstanding/BACKUP{now}rxOrdersOutstanding.csv\", index=False)\n",
    "  print(f\"Saved master list backup to file {outputDir}/BACKUP_rxOrdersOutstanding/BACKUP{now}_rxOrdersOutstanding.csv\")\n",
    "  placedCsv.to_csv(f\"{outputDir}/BACKUP_rxOrdersPlaced/BACKUP{now}rxOrdersPlaced.csv\", index=False)\n",
    "  print(f\"Saved placed orders list backup to file {outputDir}/BACKUP_rxOrdersPlaced/BACKUP{now}_rxOrdersPlaced.csv\")\n",
    "  shippedCsv.to_csv(f\"{outputDir}/BACKUP_rxOrdersShipped/BACKUP{now}rxOrdersShipped.csv\", index=False)\n",
    "  print(f\"Saved shipped orders list backup to file {outputDir}/BACKUP_rxOrdersShipped/BACKUP{now}_rxOrdersShipped.csv\")\n",
    "\n",
    "\n",
    "orders = []\n",
    "orderIdsProcessed = []\n",
    "ordersProcessed = 0\n",
    "ordersToFulfill = 0\n",
    "skipCount = 0\n",
    "hiPowerCount = 0\n",
    "changeRequestedCount = 0\n",
    "apiCallCount = 0\n",
    "\n",
    "orders = populateForcedOrders(orders)\n",
    "if(PROCESS_ALL_ORDERS):\n",
    "  orders.extend(shopifyOrders)\n",
    "\n",
    "for order in orders:\n",
    "  if(SHOW_DEBUG): print(f\"##### Processing order {order['shopifyOrder']['name']}... #####\") #debug\n",
    "  orderId = order['shopifyOrder']['name']\n",
    "  if(orderId in orderIdsProcessed):\n",
    "    continue\n",
    "  else:\n",
    "    orderIdsProcessed.append(orderId)\n",
    "    ordersProcessed += 1\n",
    "  if(PRETEND_NOT_FORCED): ### debugging feature\n",
    "    orderIsForced = False\n",
    "  else:\n",
    "    orderIsForced = 'force' in order\n",
    "  beyondOrderId = orderId\n",
    "  orderStatus = ''\n",
    "  orderGroup = ''\n",
    "  orderIpd = ''\n",
    "  orderDate = order['shopifyOrder']['created_at']\n",
    "  items = order['shopifyOrder']['line_items']\n",
    "  for item in items:\n",
    "    if(item['name'] == 'Prescription Lenses'):\n",
    "      if(SHOW_DEBUG): print(f\"- Order has prescription lenses\") #debug\n",
    "      # first, get the corresponding Beyond order\n",
    "      dateSourceOrder = order\n",
    "      beyondOrderId = getSiblingProp(beyondOrdersByEmail, order['shopifyOrder']['email'], 'email', 'orderName')\n",
    "      # if no Beyond order found in local CSV, check the AdminAPI\n",
    "      if(beyondOrderId == None):\n",
    "        backupCheck = BigApi.adminGet(f\"/admin/shop/shopify_orders?status=any&search={urllib.parse.quote(order['shopifyOrder']['email'])}\")\n",
    "        apiCallCount += 1\n",
    "        if(len(backupCheck['orders']) > 0):\n",
    "          for backupOrder in backupCheck['orders']:\n",
    "            if(backupOrder['shopifyOrder']['name'] != order['shopifyOrder']['name']):\n",
    "              beyondOrderId = backupOrder['shopifyOrder']['name']\n",
    "              dateSourceOrder = backupOrder\n",
    "      if(beyondOrderId != None):\n",
    "        if(dateSourceOrder == order):\n",
    "          dateSourceOrder = beyondOrdersByEmail[getIndexByProp(beyondOrdersByEmail, 'orderName', beyondOrderId)]\n",
    "        orderDate = dateSourceOrder['shopifyOrder']['created_at']\n",
    "        if(SHOW_DEBUG): print(f\"- Located associated Beyond order: {beyondOrderId}\") #debug\n",
    "        if(SHOW_DEBUG): print(f\"- created_at: {dateSourceOrder['shopifyOrder']['created_at']}\") #debug\n",
    "      if('bigOrder' in dateSourceOrder and 'ipd' in dateSourceOrder['bigOrder']):\n",
    "        orderIpd = f\"{actualIpd(dateSourceOrder['bigOrder']['ipd'])}mm\"\n",
    "        if(SHOW_DEBUG): print(f\"- Found 'bigOrder' in dateSourceOrder. IPD unmodified: {dateSourceOrder['bigOrder']['ipd']} -- actual: {orderIpd}.\")\n",
    "\n",
    "      if(orderIncludesTags(dateSourceOrder, forcedOrderTags)):\n",
    "        orderIsForced = True\n",
    "        if(SHOW_DEBUG): print(f\"- Order has forced tag\") #debug\n",
    "      if(orderIsForced):\n",
    "        orderGroup = 'PRIORITY'\n",
    "        orderDate = \"2023-02-13T00:06:00-08:00\" # set to the earliest possible date if forced\n",
    "      if('vrchat' in order['shopifyOrder']['tags'] or 'vrchat' in dateSourceOrder['shopifyOrder']['tags']):\n",
    "        orderGroup = 'VRCHAT'\n",
    "\n",
    "      # convert orderDate to string in format YYYY-MM-DD_HH-MM-SS in PST\n",
    "      orderDate = datetime.datetime.strptime(orderDate, \"%Y-%m-%dT%H:%M:%S%z\")\n",
    "      orderDate = orderDate.astimezone(datetime.timezone(datetime.timedelta(hours=-8)))\n",
    "      orderDate = orderDate.strftime(\"%Y-%m-%d_%H:%M:%S\")\n",
    "      if(SHOW_DEBUG): print(f\"- Order date determined: {orderDate}\") #debug\n",
    "\n",
    "      validityStatus = orderValidityStatus(dateSourceOrder, order)\n",
    "      if(SHOW_DEBUG): print(f\"- Order's validity status is: {validityStatus}\") #debug\n",
    "      if(SHOW_DEBUG): print(f\"- Result polling from rxMasterList CSV:\") #debug\n",
    "      if(SHOW_DEBUG): display(rxMasterList.get(rxMasterList['ID'] == orderId)) #debug\n",
    "      orderIsLogged = False\n",
    "      inMasterList = False\n",
    "      inPlacedList = False\n",
    "      inShippedList = False\n",
    "      if(len(shippedCsv.get(shippedCsv['ID'] == orderId).index) > 0):\n",
    "        orderIsLogged = True\n",
    "        inShippedList = True\n",
    "        csvLocation = 'shippedCsv'\n",
    "        loggedCsv = shippedCsv\n",
    "      elif(len(placedCsv.get(placedCsv['ID'] == orderId).index) > 0):\n",
    "        orderIsLogged = True\n",
    "        inPlacedList = True\n",
    "        csvLocation = 'placedCsv'\n",
    "        loggedCsv = placedCsv\n",
    "      elif(len(rxMasterList.get(rxMasterList['ID'] == orderId).index) > 0):\n",
    "        orderIsLogged = True\n",
    "        inMasterList = True\n",
    "        csvLocation = 'rxMasterList'\n",
    "        loggedCsv = rxMasterList\n",
    "      \n",
    "\n",
    "      if(orderIsLogged):\n",
    "        if(SHOW_DEBUG): print(f\"- Order was found in {csvLocation}\") #debug\n",
    "        if loggedCsv.loc[loggedCsv['ID'] == orderId, 'Order Date'].head(1).isnull().values:\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Order Date'] = orderDate\n",
    "        if loggedCsv.loc[loggedCsv['ID'] == orderId, 'Group'].head(1).isnull().values:\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Group'] = orderGroup\n",
    "          \n",
    "        odRight = loggedCsv.loc[loggedCsv['ID'] == orderId, 'Vision Rx - OD'].values[0]\n",
    "        osLeft = loggedCsv.loc[loggedCsv['ID'] == orderId, 'Vision Rx - OS'].values[0]\n",
    "        if(SHOW_DEBUG): print(f\"- OD Rx in CSV is {odRight}\") #debug\n",
    "        if pd.isna(odRight) or loggedCsv.loc[loggedCsv['ID'] == orderId, 'Vision Rx - OD'].head(1).isna().values:\n",
    "          odRight, osLeft = getOdOs(item['properties'])\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Vision Rx - OD'] = odRight\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Vision Rx - OS'] = osLeft\n",
    "        if(odRight == 'SKIP' or osLeft == 'SKIP'):\n",
    "          hiPowerCount += 1\n",
    "          if('POWER CAP' not in orderStatus):\n",
    "            orderStatus += '>POWER CAP '\n",
    "        if('Prescription Change requested' in order['shopifyOrder']['tags']):\n",
    "          changeRequestedCount += 1\n",
    "          if('CHANGE REQUESTED' not in orderStatus):\n",
    "            orderStatus += '>CHANGE REQUESTED '\n",
    "\n",
    "        if loggedCsv.loc[loggedCsv['ID'] == orderId, 'IPD'].head(1).isna().values:\n",
    "          if(orderIpd == ''):\n",
    "            if('bigOrder' in dateSourceOrder and 'ipd' in dateSourceOrder['bigOrder']):\n",
    "              orderIpd = f\"{actualIpd(dateSourceOrder['bigOrder']['ipd'])}mm\"\n",
    "              if(SHOW_DEBUG): print(f\"- Found 'bigOrder' in dateSourceOrder. IPD unmodified: {dateSourceOrder['bigOrder']['ipd']} -- actual: {orderIpd}.\")\n",
    "            elif(beyondOrderId != None and beyondOrderId != '' and beyondOrderId[0] == '#'):\n",
    "              ipdCheck = BigApi.adminGet(f\"/admin/shop/shopify_orders?status=any&search={urllib.parse.quote(beyondOrderId)}\")\n",
    "              apiCallCount += 1\n",
    "              if(len(ipdCheck['orders']) > 0):\n",
    "                for ipdSource in ipdCheck['orders']:\n",
    "                  if('bigOrder' in ipdSource and 'ipd' in ipdSource['bigOrder'] and orderIpd == ''):\n",
    "                    orderIpd = f\"{actualIpd(ipdSource['bigOrder']['ipd'])}mm\"\n",
    "                    if(SHOW_DEBUG): print(f\"- Found 'bigOrder' in ipdSource. IPD unmodified: {ipdSource['bigOrder']['ipd']} -- actual: {orderIpd}.\")\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'IPD'] = orderIpd\n",
    "        if(not inMasterList and (orderIsForced or 'prescription-ordered' not in order['shopifyOrder']['tags'])):\n",
    "          ordersToFulfill += 1\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Status'] = orderStatus\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Record Added'] = addedStr\n",
    "          if(SHOW_DEBUG): print(f\"- Returning previously placed order to rxMasterList. Current state:\") #debug\n",
    "          if(SHOW_DEBUG): display(loggedCsv.loc[loggedCsv['ID'] == orderId].head(1)) #debug\n",
    "          rxMasterList = pd.concat([rxMasterList, loggedCsv.get(loggedCsv['ID'] == orderId).head(1)])\n",
    "          loggedCsv.drop(loggedCsv.get(loggedCsv['ID'] == orderId).index, inplace=True)\n",
    "        elif(validityStatus == 'canceled' and not orderIsForced):\n",
    "          orderStatus += '>CANCELED '\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Status'] = orderStatus\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Record Added'] = addedStr\n",
    "          if(SHOW_DEBUG): print(f\"- Adding order to canceled CSV. Current state:\") #debug\n",
    "          if(SHOW_DEBUG): display(loggedCsv.loc[loggedCsv['ID'] == orderId]) #debug\n",
    "          canceledCsv = pd.concat([canceledCsv, loggedCsv.get(loggedCsv['ID'] == orderId)])\n",
    "          loggedCsv.drop(loggedCsv.get(loggedCsv['ID'] == orderId).index, inplace=True)\n",
    "        elif(validityStatus == 'fulfilled' and not inShippedList and not orderIsForced):\n",
    "          if('>SHIPPED' not in orderStatus):\n",
    "            orderStatus += '>SHIPPED '\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Status'] = orderStatus\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Record Added'] = addedStr\n",
    "          if(SHOW_DEBUG): print(f\"- Adding order to shipped CSV. Current state:\") #debug\n",
    "          if(SHOW_DEBUG): display(loggedCsv.loc[loggedCsv['ID'] == orderId]) #debug\n",
    "          shippedCsv = pd.concat([shippedCsv, loggedCsv.get(loggedCsv['ID'] == orderId).head(1)])\n",
    "          loggedCsv.drop(loggedCsv.get(loggedCsv['ID'] == orderId).index, inplace=True)\n",
    "        elif('prescription-ordered' in order['shopifyOrder']['tags'] and not inPlacedList and not inShippedList and not orderIsForced):\n",
    "          if('>ORDERED' not in orderStatus):\n",
    "            orderStatus += '>ORDERED '\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Status'] = orderStatus\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Record Added'] = addedStr\n",
    "          if(SHOW_DEBUG): print(f\"- Adding order to placed CSV. Current state:\") #debug\n",
    "          if(SHOW_DEBUG): display(loggedCsv.loc[loggedCsv['ID'] == orderId].head(1)) #debug\n",
    "          placedCsv = pd.concat([placedCsv, loggedCsv.get(loggedCsv['ID'] == orderId).head(1)])\n",
    "          loggedCsv.drop(loggedCsv.get(loggedCsv['ID'] == orderId).index, inplace=True)\n",
    "        else:\n",
    "          if(inMasterList):\n",
    "            ordersToFulfill += 1\n",
    "          loggedCsv.loc[loggedCsv['ID'] == orderId, 'Status'] = orderStatus\n",
    "          if(SHOW_DEBUG): print(f\"- Order is staying in {csvLocation}. Current state:\") #debug\n",
    "          if(SHOW_DEBUG): display(loggedCsv.loc[loggedCsv['ID'] == orderId]) #debug\n",
    "        break\n",
    "      # check this order should be added to rxMasterList\n",
    "      if(not ( # all tests below are where we want the order to pass through\n",
    "        orderIsForced or \\\n",
    "        validityStatus == 'fulfilled' or \\\n",
    "        'prescription-ordered' in order['shopifyOrder']['tags'] or \\\n",
    "        (orderGroup == 'VRCHAT' and validityStatus != 'canceled') or \\\n",
    "        (validityStatus == 'valid' and 'prescription-ordered' not in order['shopifyOrder']['tags']) \\\n",
    "      )):\n",
    "        skipCount += 1\n",
    "        break\n",
    "\n",
    "\n",
    "      if(SHOW_DEBUG): print(f\"- Order was NOT found in existing CSV lists. Proceeding to check prescription values etc.\") #debug\n",
    "      if(not orderIsForced and validityStatus == 'fulfilled'):\n",
    "        csvName = 'shipped CSV'\n",
    "        targetCsv = shippedCsv\n",
    "      elif(not orderIsForced and 'prescription-ordered' in order['shopifyOrder']['tags']):\n",
    "        csvName = 'placed CSV'\n",
    "        targetCsv = placedCsv\n",
    "      else:\n",
    "        csvName = 'rxMasterList CSV'\n",
    "        ordersToFulfill += 1\n",
    "        targetCsv = rxMasterList\n",
    "\n",
    "      odRight, osLeft = getOdOs(item['properties'])\n",
    "      if(odRight == 'SKIP' or osLeft == 'SKIP'):\n",
    "        hiPowerCount += 1\n",
    "        if('POWER CAP' not in orderStatus):\n",
    "          orderStatus += '>POWER CAP '\n",
    "      if('Prescription Change requested' in order['shopifyOrder']['tags']):\n",
    "        changeRequestedCount += 1\n",
    "        if('CHANGE REQUESTED' not in orderStatus):\n",
    "          orderStatus += '>CHANGE REQUESTED '\n",
    "      # add to corresponding list CSV\n",
    "      targetCsv.loc[len(targetCsv.index)] = [\n",
    "        orderId,\n",
    "        order['shopifyOrder']['billing_address']['name'],\n",
    "        odRight,\n",
    "        osLeft,\n",
    "        order['shopifyOrder']['email'],\n",
    "        orderDate,\n",
    "        orderStatus,\n",
    "        orderGroup,\n",
    "        f\"https://admin.shopify.com/store/bigscreenvr/orders/{order['shopifyOrder']['id']}\",\n",
    "        beyondOrderId,\n",
    "        addedStr,\n",
    "        orderIpd,\n",
    "      ]\n",
    "      if(SHOW_DEBUG): print(f\"- Order added to {csvName} in state:\") #debug\n",
    "      if(SHOW_DEBUG): display(targetCsv.loc[targetCsv['ID'] == orderId]) #debug\n",
    "\n",
    "  if(ordersProcessed % 100 == 0):\n",
    "    print(f\"{ordersProcessed} total orders processed...\")\n",
    "  if(apiCallCount % 10 == 0):\n",
    "    # time delay to avoid rate limiting\n",
    "    time.sleep(8)\n",
    "    apiCallCount += 1\n",
    "    BigApi.adminLogin()\n",
    "\n",
    "# wrap up\n",
    "print(f\"### Finished iterating through {ordersProcessed} total orders ###\")\n",
    "print(f\"A total of {ordersToFulfill} lens insert orders were counted during this run\")\n",
    "print(f\"There are a grand total of {len(rxMasterList.index)} lens insert orders to fulfill—to be saved to CSV\")\n",
    "print(f\"{skipCount} orders have been skipped (considered invalid)\")\n",
    "print(f\"{hiPowerCount} orders have exceeded the power cap\")\n",
    "print(f\"{changeRequestedCount} orders have prescription changes requested\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 5. Save CSV to rxOrdersOutstanding.csv\n",
    "today = date.today().strftime(\"%Y-%m-%d\")\n",
    "\n",
    "# save the 3 CSVs, but only if they have at least one row. the canceled CSV should contain datestamps in their filenames\n",
    "if(len(rxMasterList.index) > 0):\n",
    "  rxMasterList.to_csv(rxLensMasterCsv, index=False)\n",
    "  print(f\"Lens insert list written to file {rxLensMasterCsv}\")\n",
    "if(len(placedCsv.index) > 0):\n",
    "  placedCsv.to_csv(rxLensPlacedCsv, index=False)\n",
    "  print(f\"{len(placedCsv.index)} placed lens orders saved to file {rxLensPlacedCsv}\")\n",
    "if(len(shippedCsv.index) > 0):\n",
    "  shippedCsv.to_csv(rxLensShippedCsv, index=False)\n",
    "  print(f\"{len(shippedCsv.index)} shipped lens orders saved to file {rxLensShippedCsv}\")\n",
    "if(len(canceledCsv.index) > 0):\n",
    "  canceledCsv.to_csv(f\"{outputDir}/lensCancellations{today}.csv\", index=False)\n",
    "  print(f\"{len(canceledCsv.index)} canceled lens orders saved to file {outputDir}/lensCancellations{today}.csv\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "len(rxMasterList.index)\n",
    "# display(beyondOrdersByEmail)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# copy the rxMasterList and strip out POWER CAP orders. optionally trim the length, then output to rxOrderGroupDATE.csv\n",
    "import os\n",
    "import pandas as pd\n",
    "from datetime import date\n",
    "\n",
    "maximumOrders = 300 #0 for no limit\n",
    "today = date.today().strftime(\"%Y-%m-%d\")\n",
    "outputDir = 'C:/Users/decid/Documents/Bigscreen/LENS_ORDERS'\n",
    "# rxGroupList = rxMasterList.copy() #debug\n",
    "\n",
    "\n",
    "rxLensMasterCsv = f'{outputDir}/rxOrdersOutstanding.csv'\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(rxLensMasterCsv):\n",
    "    try:\n",
    "        rxGroupList = pd.read_csv(rxLensMasterCsv)\n",
    "        # print('Original data:', rxMasterList) #debug\n",
    "    except:\n",
    "        assert False, f\"Error: {rxLensMasterCsv} contains invalid CSV data. Please check formatting.\"\n",
    "else:\n",
    "    assert False, f\"{rxLensMasterCsv} does not exist. Please create it in said directory.\"\n",
    "\n",
    "\n",
    "# iterate over rows of rxGroupList and drop rows where Status column contains string 'POWER CAP'\n",
    "for index, row in rxGroupList.iterrows():\n",
    "  if('POWER CAP' in f\"{row['Status']}\"):\n",
    "    rxGroupList.drop(index, inplace=True)\n",
    "\n",
    "# display(rxGroupList) #debug\n",
    "\n",
    "if(maximumOrders > 0 and len(rxGroupList.index) > maximumOrders):\n",
    "  rxGroupList = rxGroupList.head(maximumOrders)\n",
    "\n",
    "rxGroupList.to_csv(f\"{outputDir}/rxOrderGroup{today}.csv\", index=False)\n",
    "print(f\"{len(rxGroupList.index)} lens orders saved to file {outputDir}/rxOrderGroup{today}.csv\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# use pandas to read in two CSVs, A and B. then compare them and output a CSV of A where all the IDs from B are removed\n",
    "import os\n",
    "import pandas as pd\n",
    "from datetime import date\n",
    "\n",
    "today = date.today().strftime(\"%Y-%m-%d\")\n",
    "outputDir = 'C:/Users/decid/Documents/Bigscreen/LENS_ORDERS'\n",
    "rxLensMasterCsv = f'{outputDir}/rxOrdersOutstanding.csv'\n",
    "rxOrderGroupCsv = f'{outputDir}/rxOrderGroup2023-11-15.csv'\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(rxLensMasterCsv):\n",
    "    try:\n",
    "        rxMasterList = pd.read_csv(rxLensMasterCsv)\n",
    "        # print('Original data:', rxMasterList) #debug\n",
    "    except:\n",
    "        assert False, f\"Error: {rxLensMasterCsv} contains invalid CSV data. Please check formatting.\"\n",
    "else:\n",
    "    assert False, f\"{rxLensMasterCsv} does not exist. Please create it in said directory.\"\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(rxOrderGroupCsv):\n",
    "    try:\n",
    "        rxGroupList = pd.read_csv(rxOrderGroupCsv)\n",
    "        # print('Original data:', rxMasterList) #debug\n",
    "    except:\n",
    "        assert False, f\"Error: {rxOrderGroupCsv} contains invalid CSV data. Please check formatting.\"\n",
    "else:\n",
    "    assert False, f\"{rxOrderGroupCsv} does not exist. Please create it in said directory.\"\n",
    "\n",
    "# iterate over rows of rxMasterList and drop rows where ID column contains string from rxGroupList\n",
    "for index, row in rxMasterList.iterrows():\n",
    "  if(row['ID'] in rxGroupList['ID'].values):\n",
    "    rxMasterList.drop(index, inplace=True)\n",
    "\n",
    "rxMasterList.to_csv(f\"{outputDir}/rxTodayOutstanding{today}.csv\", index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# use pandas to read in rxLensMasterCsv, then generate a dictionary of all the IPDs and their counts\n",
    "import os\n",
    "import pandas as pd\n",
    "from datetime import date\n",
    "\n",
    "today = date.today().strftime(\"%Y-%m-%d\")\n",
    "outputDir = 'C:/Users/decid/Documents/Bigscreen/LENS_ORDERS'\n",
    "# rxTodayOutstandingCsv = f'{outputDir}/rxOrdersOutstanding.csv'\n",
    "# rxTodayOutstandingCsv = f'{outputDir}/rxOrdersOutstanding_Q3-ONLY.csv'\n",
    "rxTodayOutstandingCsv = f'{outputDir}/rxTodayOutstanding2023-12-06.csv'\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(rxTodayOutstandingCsv):\n",
    "    try:\n",
    "        rxTodayOutstandingList = pd.read_csv(rxTodayOutstandingCsv)\n",
    "        # print('Original data:', rxTodayOutstandingList) #debug\n",
    "    except:\n",
    "        assert False, f\"Error: {rxTodayOutstandingCsv} contains invalid CSV data. Please check formatting.\"\n",
    "else:\n",
    "    assert False, f\"{rxTodayOutstandingCsv} does not exist. Please create it in said directory.\"\n",
    "\n",
    "ipdDict = {}\n",
    "for index, row in rxTodayOutstandingList.iterrows():\n",
    "  # check if ID for this row is null\n",
    "  if(pd.isna(row['ID'])):\n",
    "    continue\n",
    "  if(row['IPD'] not in ipdDict):\n",
    "    ipdDict[row['IPD']] = 0\n",
    "  ipdDict[row['IPD']] += 1\n",
    "\n",
    "# print(ipdDict)\n",
    "\n",
    "# print ipdDict with pretty formatting\n",
    "for key in ipdDict:\n",
    "  print(f\"{key}: {ipdDict[key]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# iterate over beyondOrdersByEmail and use email to pull all orders from the API. then, compare IPD from the oldest order that has IPD against IPD from the newest order that has IPD, and store the difference in a new dictionary. use the difference as the key, and increment the count for that key. then, print the dictionary with pretty formatting\n",
    "import json\n",
    "import os\n",
    "import pandas as pd\n",
    "import urllib.parse\n",
    "from datetime import date\n",
    "import datetime\n",
    "import time\n",
    "\n",
    "# If file exists, read it. If not, quit\n",
    "if os.path.exists(beyondOrdersFile):\n",
    "    try:\n",
    "        with open(beyondOrdersFile, 'r') as file:\n",
    "            beyondOrdersByEmail = json.load(file)\n",
    "            if(SHOW_DEBUG): print(f\"Library of Beyond orders loaded from {beyondOrdersFile}.\")\n",
    "        # print('Original data:', beyondOrdersByEmail) #debug\n",
    "    except json.JSONDecodeError:\n",
    "        assert False, f\"Error: {beyondOrdersFile} contains invalid JSON. Please check formatting.\"\n",
    "else:\n",
    "    print(f\"{beyondOrdersFile} does not exist. Populate and write it using the cell above.\")\n",
    "\n",
    "\n",
    "ipdDiffDict = {}\n",
    "ordersProcessed = 0\n",
    "\n",
    "shopifyOrdersByEmail = {}\n",
    "## populate shopifyOrdersByEmail by iterating over shopifyOrders and adding each order to the dictionary with the email as the key\n",
    "for order in shopifyOrders:\n",
    "  if(order['shopifyOrder']['email'] not in shopifyOrdersByEmail):\n",
    "    shopifyOrdersByEmail[order['shopifyOrder']['email']] = []\n",
    "  shopifyOrdersByEmail[order['shopifyOrder']['email']].append(order)\n",
    "\n",
    "for order in beyondOrdersByEmail:\n",
    "  if(order['email'] == ''):\n",
    "    continue\n",
    "  if(order['email'] in shopifyOrdersByEmail):\n",
    "    earliestIPD = -1\n",
    "    latestIPD = -1\n",
    "    for order in shopifyOrdersByEmail[order['email']]:\n",
    "      if('bigOrder' in order and 'ipd' in order['bigOrder']):\n",
    "        if(earliestIPD == -1):\n",
    "          earliestIPD = order['bigOrder']['ipd']\n",
    "        else:\n",
    "          latestIPD = order['bigOrder']['ipd']\n",
    "    if(earliestIPD != -1 and latestIPD != -1):\n",
    "      ipdDiff = f\"{latestIPD - earliestIPD}\"\n",
    "      if(ipdDiff not in ipdDiffDict):\n",
    "        ipdDiffDict[ipdDiff] = 0\n",
    "      ipdDiffDict[ipdDiff] += 1\n",
    "      ordersProcessed += 1\n",
    "  if(ordersProcessed % 250 == 0):\n",
    "    print(f\"{ordersProcessed} total orders processed...\")\n",
    "  # if(ordersProcessed % 250 == 0):\n",
    "  #   # eight second time delay to avoid rate limiting\n",
    "  #   time.sleep(8)\n",
    "  #   BigApi.adminLogin()\n",
    "  \n",
    "\n",
    "# print(ipdDiffDict)\n",
    "\n",
    "# print ipdDiffDict with pretty formatting\n",
    "print(f\"Compiled IPD diffs for {ordersProcessed} total orders.\")\n",
    "\n",
    "#sort the dictionary by key, lowest to highest\n",
    "ipdDiffDict = dict(sorted(ipdDiffDict.items()))\n",
    "\n",
    "for key in ipdDiffDict:\n",
    "  print(f\"{key}: {ipdDiffDict[key]}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "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.10.11"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
