import time
from datetime import datetime

import pandas as pd
import requests

def fr24(timestamp, page=1, airport='tpe'):
    headers = {
        'authority': 'api.flightradar24.com',
        'accept': 'application/json',
        'origin': 'https://www.flightradar24.com',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36',
        'dnt': '1',
        'sec-fetch-site': 'same-site',
        'sec-fetch-mode': 'cors',
        'referer': 'https://www.flightradar24.com/airport/tpe/arrivals',
        'accept-language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7',
    }

    params = (
        ('code', airport),
        ('plugin/[/]', 'schedule'),
        ('plugin-setting/[schedule/]/[mode/]', 'departures'),
        ('plugin-setting/[schedule/]/[timestamp/]', timestamp),
        ('page', page),
        ('limit', '100'),
        ('token', ''),
    )

    response = requests.get(
        'https://api.flightradar24.com/common/v1/airport.json', headers=headers, params=params)

    j = response.json()
    data = j['result']['response']['airport']['pluginData']['schedule']['departures']['data']

    return data

def parse(data, origin):
    SULEM = []

    for v in data:
        try:
            callsign = v['flight']['identification']['number']['default']
            icao = v['flight']['airline']['code']['icao']
            callsign = icao+callsign[2:]
            model = v['flight']['aircraft']['model']['code']

            dest_code = v['flight']['airport']['destination']['code']['icao']
            icao = {'tpe': 'RCTP', 'tsa': 'RCSS', 'khh': 'RCKH', 'rmq': 'RCMQ'}
            flytime = {'tpe': 30*60, 'tsa': 25*60, 'khh': 50*60, 'rmq': 40*60}
            # status = flight['flight']['status']['text']

            if 'ZSPD' == dest_code or 'ZSSS' == dest_code:
                dep_t = v['flight']['time']['estimated']['departure']
                dt = datetime.fromtimestamp(dep_t).strftime("%H:%M")
                boundry_t = dep_t + flytime[origin]
                st = datetime.fromtimestamp(boundry_t).strftime("%H:%M")

                row = {'cs': callsign,
                       'type': model,
                       'dep': dep_t,
                       'slm time': boundry_t,
                       'from': icao[origin],
                       'dt': dt,
                       'to': dest_code,
                       'est slm': st}
                SULEM.append(row)
        except TypeError:
            pass

    return SULEM

def main():
    SULEM = []
    airports = ['tpe', 'tsa', 'khh', 'rmq']
    for airport in airports:
        for page in range(1, 2):
            timestamp = int(time.time() - 2*60*60)
            data = fr24(timestamp, page, airport)
            flights = parse(data, airport)
            SULEM.extend(flights)

    df = pd.DataFrame(SULEM)
    df.sort_values(by=['est slm'], inplace=True)
    print(df)

if __name__ == "__main__":
    main()


#!/usr/bin/env python3

"""
PURPOSE:
    - To create a real-time FILTERED stream of Twitter
    data, utilizing the Twitter V1 API.

INPUT:
    - A file with keywords/hashtags (one per line) that
    will be utilized as the filters matching to tweets
    in real-time.

OUTPUT:
    - One JSON file is created - per day - where each
    line item represents one tweet object.

DEPENDENCIES:
    - Tweepy (https://www.tweepy.org/)

Author: Matthew R. DeVerna
Date: 12/29/2020

CHANGELOG:
    - 01/02/2020: Removed the scripts use of emailer.py
    email updating system functionality to simplify the
    script.

"""

# Import packages
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

import os
import sys
import argparse
import logging
from datetime import datetime as dt

# Dependencies
from tweepy import OAuthHandler, Stream, StreamListener

# Initialize the log
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# Get current time
now = dt.strftime(dt.now(), '%m-%d-%Y_%H-%M-%S')

# Define file handler and set logger formatter
log_filename = f"{now}_stream.log"

# Set logging configuration
logging.basicConfig(
    filename= log_filename,
    format='%(levelname)s - %(asctime)s | %(message)s',
    datefmt= "%m-%d-%Y_%H-%M-%S",
    level= logging.INFO)

# Set your Twitter tokens.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
logging.info(f"Script begins: {now}")
logging.info("Loading Twitter API Keys/Tokens...")

# Go to http://apps.twitter.com and create an app.
# The consumer key and secret will be generated for you after
api_key = os.environ.get("TWITTER_API_KEY")
api_key_secret = os.environ.get("TWITTER_API_KEY_SECRET")

# After the step above, you will be redirected to your app's page.
# Create an access token under the the "Your access token" section
access_token = os.environ.get("TWITTER_ACCESS_TOKEN")
access_token_secret = os.environ.get("TWITTER_ACCESS_TOKEN_SECRET")

# Set CLI Arguments.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

logging.info("Parsing Command Line Arguments...")

# Initiate the parser w. a simple description.
parser = argparse.ArgumentParser(
  description="Scrape real-time tweets from Twitter using the V1 API based on keywords passed via file."
  )
# Add optional arguments
parser.add_argument(
  "-f", "--file",
  metavar='File',
  help="Full path to the file containing keywords you would like to include. (One object (i.e. hashtag) per line)"
  )

# Read parsed arguments from the command line into "args"
args = parser.parse_args()

# Assign them to objects
file = args.file

# Build Functions.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class Listener(StreamListener):
    """
    The Listener wraps tweepys StreamListener allowing customizable handling
    of tweets and errors received from the stream.

    This Listener does the following:
        - Writes raw tweet data to a file named for the day it is scraped.
    """

    def on_data(self, data):
        today = dt.strftime(dt.now(), "%m-%d-%Y")
        file_name = f"data/streaming_data--{today}.json"

        """
        The 'if os.path.isfile()' line below checks if the data
        file has already been created for a given day. If not,
        we send an email update which includes an update on
        the number of total tweets collected as well as the
        number of tweets collected that day.

        Since a new file will be created at the beginning of
        each day, this if statement will only be activated
        once at the very end of of day.

        This allows us to get updates on the volume of tweets
        we are capturing and get an idea of when a surge
        may be taking place.
        """
        if os.path.isfile(file_name):
            with open(file_name, "a+") as f:
                f.write(f"{data}")

        else:
            logging.info(f"Creating file: {file_name}")

            with open(file_name, "a+") as f:
                f.write(f"{data}")

        return True

    def on_error(self, status_code):
        # Log error with exception info
        logging.error(f"Error, code {status_code}", exc_info=True)
        if status_code == 420:

            # Wait five minutes
            time.sleep(300)
            return True

def load_terms(file):
    logging.info("Attempting to load filter rules...")

    filter_terms = []
    with open(file, "r") as f:
        for line in f:
            logging.info("Loaded Filter Rule: {}".format(line.strip('\n')))
            filter_terms.append(line.strip("\n"))

    return filter_terms

# Execute main program.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

if __name__ == '__main__':
    # Create data dir if it doesn't exist already
    try:
        os.makedirs("data")
    except:
        pass

    # Loading the file terms...
    filter_terms = load_terms(file)

    # Set up the stream.
    logging.info("Setting up the stream...")
    listener = Listener()
    auth = OAuthHandler(api_key, api_key_secret)
    auth.set_access_token(access_token, access_token_secret)
    stream = Stream(auth, listener)

    # Begin the stream.
    logging.info("Starting the stream...")
    while True:
        try:
            stream.filter(track=filter_terms, languages=["en"])
        except KeyboardInterrupt:
            logging.info("User manually ended stream with a Keyboard Interruption.")
            sys.exit("\n\nUser manually ended stream with a Keyboard Interruption.\n\n")
        except Exception as e:
            logging.debug('Unexpected exception: %s %e')
            continue


import sys

def incoming_edge(node, graph):
    for k in graph:
        if node in graph[k]:
            return True

    return False

def topological_sort(no_incoming, graph):
    sorted_elems = []

    while len(no_incoming) > 0:
        node = no_incoming[0]
        del no_incoming[0]

        sorted_elems.append(node)

        if node in graph:
            destinations = graph[node]

            while len(destinations) > 0:
                d = destinations[0]
                destinations.remove(d)

                if not incoming_edge(d, graph):
                    no_incoming.append(d)
                    no_incoming.sort()

    return sorted_elems

def read(filename):
    graph = {}
    with open(filename) as f:
        for line in f:
            id1 = line[5:line.find(' ', 5)]
            idx = line.find('step') + 5
            id2 = line[idx:line.find(' ', idx)]

            if id1 not in graph:
                graph[id1] = []

            graph[id1].append(id2)

    no_incoming = []
    for node in graph:
        if (not incoming_edge(node, graph)):
            no_incoming.append(node)

    sorted_elems = topological_sort(no_incoming, graph)
    print(''.join(sorted_elems))

if __name__ == '__main__':
    read(sys.argv[1])


from django.db import connection, reset_queries
import time
import functools

def query_debugger(func):
    """
    Decorator to debug performance of SQL queries. Results are reported in the
    format:

    Function:
    Number of Queries:
    Finished in:
    """

    @functools.wraps(func)
    def inner_func(*args, **kwargs):

        reset_queries()

        start_queries = len(connection.queries)

        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()

        end_queries = len(connection.queries)

        print("Function: {}".format(func.__name__))
        print("Number of Queries: {}".format(end_queries - start_queries))
        print("Finished in: {:.2f}s".format((end - start)))
        return result

    return inner_func


import os
from argparse import ArgumentParser
import operator

class File:
    """File object that can store file attributes(file size, file path, etc).
    This has class dictionary that will store summary of all file types. """

    file_dic = {}

    def __init__(self, file):
        self._path, self._filename = os.path.split(file)
        self._file = file
        self._size = os.path.getsize(file)
        self._extension = os.path.splitext(self._filename)[1][1:]
        self.count_files()

    def get_file_extension(self):
        '''Since git have multiple dots(.) this function checks it.
        and returns the file extension.'''
        if self.file().count('.') > 1:
            file_extension = 'git'
            return file_extension
        else:
            return self._extension

    def path(self):
        return self._path

    def file(self):
        return self._file

    def size(self):
        return self._size

    def filename(self):
        return self._filename

    def count_files(self):
        '''Count files with the same extension, sum its size then store both
        count and size in a list and store the list in a dictionary
        with the key of file extension.'''
        if self._extension not in File.file_dic:
            File.file_dic[self._extension] = [1, self.size()]
        else:
            File.file_dic[self._extension][0] += 1
            File.file_dic[self._extension][1] += self.size()

def format_size(size):
    '''Format the different file sizes into B, KB, MB and GB.
    Returns the formatted size like '8.5 GB' and '5 MB'.'''
    size = remove_zfill(size)
    if len(size) <= 3:
        size = size + ' B'
        return size.rjust(9)
    elif len(size) <= 6:
        size = round(int(size)/1_000, 2)
        size = str(size) + ' KB'
        return size.rjust(9)
    elif len(size) <= 9:
        size = round(int(size)/1_000_000, 2)
        size = str(size) + ' MB'
        return size.rjust(9)
    else:
        size = round(int(size)/1_000_000_000, 2)
        size = str(size) + ' GB'
        return size.rjust(9)

def remove_zfill(size):
    '''Remove all the zeros added that were help for sorting.'''
    if not size.startswith('0'):
        return size
    for char in size:
        if len(size) == 1:
            return size
        if char == '0':
            return remove_zfill(size[1:])

def file_gen(folder_path):
    '''Generate all files complete with each path in the given folder and its subfolders.'''
    for path, _, files, in os.walk(folder_path):
        for file in files:
            file_ = os.path.join(path, file)
            if not os.path.isfile(file_):
                continue
            yield file_

def write_to_file(folder, all_files, out_file):
    '''Write to a text file the summary of all files present.'''
    if not out_file[-3:] == 'txt':
        out_file = out_file + '.txt'

    with open(os.path.join(folder, out_file), 'w', encoding='utf8') as text_file:
        text_file.write('List of all files inside folders and its subfolders in "{}".\n\n'.upper().format(folder))

        for size, file in all_files:
            size = format_size(size)
            line = '{} - {}\n'.format(size, file)
            text_file.write(line)

        summary = '*********** SUMMARY ***********'.center(53)
        text_file.write('\n\n' + summary + '\n\n')

        t_size = 'Total Size'.center(17).upper()
        n_files = 'Number of Files'.center(17).upper()
        file_ = 'File'.center(17).upper()
        text_file.write(t_size + '|' + n_files + '|' + file_ + '\n')

        file_dic = dict(sorted(File.file_dic.items(), key=operator.itemgetter(1), reverse=True))

        for file_type, value in file_dic.items():
            num, size = value
            size = format_size(str(size)).center(17)
            file_type = test_if_hash(file_type)
            file_type = file_type.center(17)
            num = str(num).center(17)
            text_file.write(size + '|' + num + '|' + file_type + '\n')

def test_if_hash(file_type):
    '''Test if if the seen file is a hash.'''
    if len(file_type) == 0:
        return 'hash'
    else:
        return file_type

def main(folder, file_ext, del_flag, org_flag, out_file):

    all_files = []

    for file in file_gen(folder):
        # print(file)
        file_obj = File(file)

        # Checks if file extension is given and then compares it with the searched file extension
        if file_ext and not file_obj._extension == file_ext:
            continue
        all_files.append((str(file_obj.size()).zfill(12), file_obj.file()))
        if del_flag:
            os.remove(file)

    if org_flag:
        all_files.sort(key=lambda x: x[1])
        write_to_file(folder, all_files, out_file)
    else:
        all_files.sort(reverse=True)
        write_to_file(folder, all_files, out_file)

if __name__ == '__main__':

    my_parser = ArgumentParser(prog='Search files',
                               usage='Search files in a given directory. May choose specific filetypes. utilities -path "D:\Documents" -ext pdf -o pdf.txt -org',
                               description='Command Line Application searches a given directory then list and ' \
                               'summarize and write on a text file.',)
    my_parser.add_argument('-path', type=str,
                                   default=os.curdir,
                                   help='dir/folder to be search of files')
    my_parser.add_argument('-ext', '--file_extension', type=str,
                                                   default=None,
                                                   help='Optional file extension that will be search')
    my_parser.add_argument('-del', '--delete',
                                         action='store_true',
                                         help='Delete all files with the specified file extension')
    my_parser.add_argument('-out', '--output', type=str,
                                            default='output.txt',
                                            help= 'Output file where reports will be saved')
    my_parser.add_argument('-org', '--organize', action='store_true',
                                                 help= 'Organize files and put different files into each folders in the summary, default is organized by size.')
    args = my_parser.parse_args()

    main(args.path, args.file_extension, args.delete, args.organize, args.output)


import numpy as np
import matplotlib.pyplot as plt

corr = np.load("./csv/corr_0123_mask.npy")
print(corr.shape)

corr = corr.T

plt.figure(figsize = (15,10))

for i in range(256):
    plt.plot(corr[i],color='grey')

plt.plot(corr[true_key],color='red')
plt.show()
plt.savefig("./image/corr_0123_mask.png")


#!/usr/bin/python
# encoding: utf-8

"""
@version: ?
@time: 2017/7/12 15:19
"""

import requests
from lxml import html

def func():
    pass

class Main():
    def __init__(self):
        pass

if __name__ == '__main__':

    names = [u"苏黎世中国", u"爱和谊日生同和(中国)", u"阳光财险重庆分公司", u"现代财产保险", u"安信农险", u"浙商保险", u"中华财险", u"长安责任险山东分公司", u"人保财险西藏分公司",
             u"人保财险河北分公司", u"长安责任险湖南分公司", u"阳光财险河南分公司", u"人保财险山西分公司", u"长安责任险福建分公司", u"平安健康险", u"阳光财险辽宁分公司",
             u"东京海上日动(中国)", u"人保财险山东分公司", u"人保财险江西分公司", u"长安责任险(浙江)", u"人保财险辽宁分公司", u"劳合社保险中国", u"中国人寿(海外)",
             u"人保财险湖北分公司", u"阳光农险", u"安邦保险集团", u"中煤保险", u"阳光财险宁波分公司", u"中国人保寿险", u"人保财险(陕西)", u"人保财险广东分公司", u"太平保险",
             u"中国人寿养老险公司", u"人保财险上海分公司", u"陆家嘴国泰人寿", u"中航安盟财险", u"阳光财险山东分公司", u"阳光财险云南分公司", u"人保财险青岛分公司", u"平安产险",
             u"阳光财险青海分公司", u"国元保险", u"人保财险贵州分公司", u"阳光财险四川分公司", u"人保财险安徽分公司", u"人保财险海南分公司", u"阳光财险江苏分公司", u"渤海保险",
             u"英大财险", u"阳光财险宁夏分公司", u"人保财险内蒙古分公司", u"人保财险江苏分公司", u"鼎和保险", u"中银三星人寿", u"鑫安保险公司", u"人保财险河南分公司", u"新华保险",
             u"交银康联", u"阳光财险北京分公司", u"太保产险", u"英大人寿", u"人保财险厦门分公司", u"人保财险湖南分公司", u"德华安顾人寿", u"都邦保险", u"阳光财险山西分公司",
             u"君康人寿", u"人保财险天津分公司", u"天安财险", u"人保财险青海分公司", u"人保财险吉林分公司", u"中国大地保险", u"人保财险云南分公司", u"人保财险大连分公司",
             u"人保财险重庆分公司", u"阳光财险上海分公司", u"人保财险浙江分公司", u"阳光财险新疆分公司", u"乐爱金产险", u"日本财险(中国)", u"人保财险甘肃分公司", u"人保财险黑龙江分公司",
             u"富德产险", u"长安责任险宁波支公司", u"人保财险广西分公司", u"安诚保险", u"三星财产(中国)", u"中美大都会人寿保险", u"阳光财险浙江分公司", u"人保财险四川分公司",
             u"锦泰保险", u"三井住友海上火灾险", u"阳光产险", u"人保财险宁波分公司", u"人保财险深圳分公司", u"永安保险公司", u"泰山保险", u"人保财险新疆分公司", u"阳光财险贵州分公司",
             u"太保寿险", u"华安保险", u"阳光财险天津分公司", u"诚泰保险", u"安盛中国", u"人保财险(福建)", u"中国财险", u"人保财险北京分公司", u"阳光财险广东分公司",
             u"华农保险", u"招商信诺", u"长安责任险江苏分公司", u"人保财险宁夏分公司"]

    URL = "http://insurance.jrj.com.cn/html/ic/list/ics-%s.shtml"
    aXpath = "//div[@class='cl1']//strong/a"
    start = 18
    end = 0
    for i in range(end, start):
        url = URL % i
        tree = html.fromstring(requests.get(url).text)
        _as = tree.xpath(aXpath)
        for a in _as:
            text = a.text.strip()
            for name in names:
                if text == name:
                    print( text + "\t", "http://insurance.jrj.com.cn" + a.get('href') + "\t")

    _url = "http://insurance.jrj.com.cn/html/ic/list/ics.shtml"
    tree = html.fromstring(requests.get(_url).text)
    _as = tree.xpath(aXpath)
    for a in _as:
        text = a.text.strip()
        for name in names:
            if text == name:
                print (text + "\t", "http://insurance.jrj.com.cn" + a.get('href') + "\t")


import numpy as np

def qr(A):
    m, n = A.shape
    Q = np.eye(m)
    for i in range(n - (m == n)):
        H = np.eye(m)
        H[i:, i:] = householder(A[i:, i])
        Q = np.dot(Q, H)
        A = np.dot(H, A)
    return Q, A

def householder(a):
    v = a / (a[0] + np.copysign(np.linalg.norm(a), a[0]))
    v[0] = 1
    H = np.eye(a.shape[0])
    H -= (2 / np.dot(v, v)) * np.dot(v[:, None], v[None, :])
    return H

x = np.array([[2, 1, 3],
              [-1, 0, 7],
              [0, -1, -1]])

q, r = qr(x)
print('Q:\n', q.round(8))
print('R:\n', r.round(8))

print("with numpy:")
q, r = np.linalg.qr(x)
print('Q:\n', q)
print('R:\n', r)


"""empty message

Revision ID: b492e1cf9101
Revises: e7124aca6e22
Create Date: 2016-08-12 17:16:37.842120

"""

# revision identifiers, used by Alembic.
revision = 'b492e1cf9101'
down_revision = 'e7124aca6e22'

from alembic import op
import sqlalchemy as sa

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('users', sa.Column('update_date', sa.DateTime(), nullable=True))
    op.add_column('users', sa.Column('update_ip', sa.String(length=16), nullable=True))
    ### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('users', 'update_ip')
    op.drop_column('users', 'update_date')
    ### end Alembic commands ###


# this is a redo of main.py because there were so many ways i could have improved my code. mainly i didnt pass any variables through
# functions. i feel like im lacking on that aspect.

import os
import random

from art import logo

play = 'y'
cards = [11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10]

def clr():
    os.system('clear')

def deal_card():
    """picks random card from cards list returns int"""
    card = random.choice(cards)
    return card

def calc_score(deck):
    """calculates current score of list of cards and handles ace card functinoallity"""
    if sum(deck) == 21 and len(deck) == 2:
        return 0
    if 11 in deck and sum(deck) > 21:
        deck.remove(11)
        deck.append(1)
    return sum(deck)

def compare(p1_score, ai_score):
    """winning logic of game. who wins who loses"""

    if p1_score > 21 and ai_score > 21:
        return "You went over. You lose 😤"
    if p1_score == ai_score:
        return "Draw 🙃"
    elif ai_score == 0:
        return "Lose, opponent has Blackjack 😱"
    elif p1_score == 0:
        return "Win with a Blackjack 😎"
    elif p1_score > 21:
        return "You went over. You lose 😭"
    elif ai_score > 21:
        return "Opponent went over. You win 😁"
    elif p1_score > ai_score:
        return "You win 😃"
    else:
        return "You lose 😤"

def play_game():
    """contains game logic"""

# initializing game
    clr()
    print(logo)
    global play
    ai_deck = [deal_card(), deal_card()]
    p1_deck = [deal_card(), deal_card()]
    print(f"Dealer Score: {ai_deck[0]} Dealer Hand: {ai_deck[0]}, ?")
    print(f"Your Score: {sum(p1_deck)} Your Hand: {p1_deck}")
    p1_score = calc_score(p1_deck)
    if p1_score == 0:
        hit = 'n'

# does p1 want more cards
    hit = input('Do you want another card? y/n\n').lower()
    while hit == 'y':
        p1_deck.append(deal_card())
        p1_score = calc_score(p1_deck)
        clr()
        print(logo)
        print(f"Dealer Score: {ai_deck[0]} Dealer Hand: {ai_deck[0]}, ?")
        print(f"Your Score: {sum(p1_deck)} Your Hand: {p1_deck}")
        hit = input('Do you want another card? y/n\n')
        if p1_score == 0 or p1_score > 21:
            hit = 'n'

# ai deck logic
    ai_score = calc_score(ai_deck)
    while ai_score != 0 and ai_score < 17:
        ai_deck.append(deal_card())
        ai_score = calc_score(ai_deck)

# win or lose
    clr()
    print(logo)
    print(f"Dealer Score: {ai_score} Dealer Hand: {ai_deck}")
    print(f"Your Score: {sum(p1_deck)} Your Hand: {p1_deck}")
    print(compare(p1_score, ai_score))

# end, play again?
    play = input('\nDo you want to play again? y/n\n')

clr()
play = input('Do you want to play a game of Black Jack? y/n\n').lower()
while play == 'y':
    play_game()


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def helper(self, node):
        if not node:
            return False
        if self.helper(node.left):
            return True
        if self.tmp == self.p:
            self.res = node
            return True
        else:
            self.tmp = node
        if self.helper(node.right):
            return True
        return False

    def inorderSuccessor(self, root: TreeNode, p: TreeNode) -> Optional[TreeNode]:
        # use two variable and do inorder traversal. tmp to store the previous node and res store the current one
        self.p = p
        self.tmp = None
        self.res = None
        self.helper(root)
        return self.res


#!/usr/bin/python
# -*- coding: utf-8 -*-

import queue

import multiprocessing

import magic
import logging
import dbApi
import signal

import traceback
import os.path

import random
random.seed()

import tqdm

import scanner.runState
import UniversalArchiveInterface as uar
import scanner.hashFile as hasher

IMAGE_EXTS = ("bmp", "eps", "gif", "im", "jpeg", "jpg", "msp", "pcx", "png", "ppm", "spider", "tiff", "webp", "xbm")
ARCH_EXTS = ("zip", "rar", "cbz", "cbr", "7z", "cb7")

class HashEngine(object):

	def __init__(self, inputQueue, outputQueue, threads=2, pHash=True, integrity=True):
		self.log           = logging.getLogger("Main.HashEngine")
		self.tlog          = logging.getLogger("Main.HashEngineThread")
		self.hashWorkers   = threads
		self.inQ           = inputQueue
		self.outQ          = outputQueue
		self.archIntegrity = integrity

		self.runStateMgr   = multiprocessing.Manager()
		self.manNamespace  = self.runStateMgr.Namespace()
		self.dbApi         = self.getDbConnection()

	def getDbConnection(self):
		'''
		Intended to be overridden in unit-tests
		'''
		return dbApi.DbApi()

	def runThreads(self):
		self.manNamespace.stopOnEmpty = False
		self.manNamespace.run = True
		args = (self.inQ,
			self.outQ,
			self.manNamespace,
			self.archIntegrity)

		self.pool = multiprocessing.pool.Pool(processes=self.hashWorkers, initializer=createHashThread, initargs=args)

	def close(self):
		self.log.info("Closing threadpool")
		self.manNamespace.run = False
		self.pool.terminate()

	def haltEarly(self):
		self.manNamespace.run = False

	def gracefulShutdown(self):
		self.manNamespace.stopOnEmpty = True

		self.pool.close()
		self.pool.join()

	def cleanPathCache(self, fqPathBase):

		self.log.info("Querying for all files on specified path.")

		itemsCursor = self.dbApi.getUniqueOnBasePath(fqPathBase)
		items = []
		retItems = 0
		for item in tqdm.tqdm(itemsCursor):
			retItems += 1
			items.append(item[0])
			if not scanner.runState.run:
				print("Breaking due to exit flag")
				return

		self.log.info("Looking for files in the DB that are not on disk anymore.")

		self.log.info("Recieved items = %d", retItems)
		self.log.info("total unique items = %s", len(items))

		for itemPath in tqdm.tqdm(items, desc="Exist check"):
			if not os.path.exists(itemPath):
				self.log.info("Item %s does not exist. Should delete from DB", itemPath)
				self.dbApi.deleteBasePath(itemPath)

			try:
				if not scanner.runState.run:
					print("Breaking due to exit flag")
					return
			except BrokenPipeError:
				self.log.error("Runstate thread exited? Halting")
				return

			self.outQ.put("clean")

def createHashThread(inQueue, outQueue, runMgr, integrity):
	# Make all the thread-pool threads ignore SIGINT, so they won't freak out on CTRL+C
	signal.signal(signal.SIGINT, signal.SIG_IGN)

	runner = HashThread(inQueue, outQueue, runMgr, integrity)
	runner.run()

class HashThread(object):

	loggerPath = "Main.HashEngine"

	def __init__(self, inputQueue, outputQueue, runMgr, integrity=True):

		# If we're running as a multiprocessing thread, inject that into
		# the logger path
		threadName = multiprocessing.current_process().name
		if threadName:
			self.tlog      = logging.getLogger("%s.%s" % (self.loggerPath, threadName))
		else:
			self.tlog      = logging.getLogger(self.loggerPath)

		self.runMgr        = runMgr
		self.inQ           = inputQueue
		self.outQ          = outputQueue
		self.archIntegrity = integrity

		self.dbApi         = self.getDbConnection()

	def getDbConnection(self):
		'''
		Intended to be overridden in unit-tests
		'''
		return dbApi.DbApi()

	def putProgQueue(self, value):
		if self.outQ:
			self.outQ.put(value)

	def run(self):
		try:
			while self.runMgr.run:
				try:
					filePath, fileName = self.inQ.get(timeout=0.5)
					# self.tlog.info("Scan task! %s", filePath)
					self.processFile(filePath)
				except queue.Empty:
					if self.runMgr.stopOnEmpty:
						self.tlog.info("Hashing thread out of tasks. Exiting.")
						break
				# self.tlog.info("HashThread loopin! stopOnEmpty = %s, run = %s", self.runMgr.stopOnEmpty, self.runMgr.run)
		except FileNotFoundError:
			print("Multiprocessing manager shut down?")

		except BrokenPipeError:
			print("Multiprocessing manager shut down?")

		self.tlog.info("Scanner exiting.")

		self.inQ.close()
		self.outQ.close()

		self.inQ.join_thread()
		self.outQ.join_thread()

	def scanArchive(self, archPath, archData):
		# print("Scanning archive", archPath)

		added_items = 0

		archIterator = uar.ArchiveReader(archPath, fileContents=archData)

		fnames = [item[0] for item in archIterator]
		fset = set(fnames)
		if len(fnames) != len(fset):
			print(fnames)
			print(fset)
			raise ValueError("Wat?")

		self.dbApi.begin()

		try:
			for fName, fp in archIterator:

				fCont = fp.read()

				fName, hexHash, pHash, imX, imY = hasher.hashFile(archPath, fName, fCont)

				insertArgs = {
							"fsPath"       :archPath,
							"internalPath" :fName,
							"itemHash"     :hexHash,
							"pHash"        :pHash,
							"imgX"         :imX,
							"imgY"         :imY
						}

				self.dbApi.insertIntoDb(**insertArgs)
				self.putProgQueue("processed")

				added_items += 1

				if not scanner.runState.run:
					break
		except:
			print(archPath)
			self.dbApi.rollback()
			raise
		finally:
			archIterator.close()

		self.tlog.info("Added %s hashes to DB from archive", added_items)
		self.dbApi.commit()

	def processImageFile(self, wholePath, dbFilePath):

		scan = True
		have = self.dbApi.getItem(fspath=dbFilePath, wantCols = ['phash', 'imgx', 'imgy'])
		if have and all(have):
			scan = False

		# print("Have hashes - ", dummy_itemHash, pHash, dHash)
		if scan:
			with open(wholePath, "rb") as fp:
				fCont = fp.read()
				try:

					fName, hexHash, pHash, imX, imY = hasher.hashFile(wholePath, "", fCont)

					insertArgs = {
								"fsPath"       :wholePath,
								"internalPath" :fName,     # fname == '' in this case
								"itemHash"     :hexHash,
								"pHash"        :pHash,
								"imgX"         :imX,
								"imgY"         :imY
							}

					# insert or update data row
					self.dbApi.upsert(**insertArgs)

					self.outQ.put("processed")

				except (IndexError, UnboundLocalError):
					self.tlog.error("Error while processing fileN")
					self.tlog.error("%s", wholePath)
					self.tlog.error("%s", traceback.format_exc())

				# self.log.info("Scanned bare image %s, %s, %s", fileN, pHash, dHash)

		else:
			self.outQ.put("skipped")

	def hashBareFile(self, wholePath, dbPath, doPhash=True):
		with open(wholePath, "rb") as fp:
			fCont = fp.read()

		fName, hexHash, pHash, imX, imY = hasher.hashFile(wholePath, "", fCont)

		insertArgs = {
					"fsPath"       :wholePath,
					"internalPath" :fName,     # fname == '' in this case
					"itemHash"     :hexHash,
					"pHash"        :pHash,
					"imgX"         :imX,
					"imgY"         :imY
				}

		self.dbApi.insertIntoDb(**insertArgs)

		self.putProgQueue("processed")

	def getFileMd5(self, wholePath):

		with open(wholePath, "rb") as fp:
			fCont = fp.read()
		hexHash = hasher.getMd5Hash(fCont)
		return hexHash, fCont

	def processArchive(self, wholePath):

		fType = "none"
		fCont = None
		archRow = self.dbApi.getItemsOnBasePathInternalPath(wholePath, "")

		contHashes = self.dbApi.getItemsOnBasePath(wholePath)

		haveImInfo = [(bool(item['imgx']) and bool(item['imgy'])) for item in contHashes if item['pHash']]

		if not all(haveImInfo):
			self.tlog.info("Missing image size information for archive %s. Rescanning.", wholePath)
			self.dbApi.deleteBasePath(wholePath)

		elif len(archRow) > 1:
			# print("archRow", archRow)
			raise ValueError("Multiple hashes for a single file? Wat?")
		elif archRow and archRow[0]['itemhash']:

			if not self.archIntegrity:
				self.putProgQueue("skipped")
				return
			item = archRow.pop()
			curHash, fCont = self.getFileMd5(wholePath)

			if curHash == item['itemhash']:
				# print("Skipped", wholePath)
				self.putProgQueue("hash_match")
				return
			else:
				self.tlog.warn("Archive %s has changed! Rehashing!", wholePath)
				self.dbApi.deleteBasePath(wholePath)

		else:
			if archRow:
				self.tlog.info("Missing whole archive hash! Rescanning!")
			self.dbApi.deleteBasePath(wholePath)
			curHash, fCont = self.getFileMd5(wholePath)

			insertArgs = {
						"fsPath"       :wholePath,
						"internalPath" :'',
						"itemHash"     :curHash
					}
			self.dbApi.insertIntoDb(**insertArgs)
			self.putProgQueue("processed")

		# TODO: Use `fCont` to prevent having to read each file twice.

		try:
			fType = magic.from_file(wholePath, mime=True)
			if not isinstance(fType, str):
				fType = fType.decode("ascii")

			# So some versions of libmagic return application/CDFV2-corrupt for some
			# thumbs.db, while some return application/CDFV2 for the /same/ file.
			# In any event, I've never seen a application/CDFV2 file that wasn't
			# one of the garbage application/CDFV2 file, so just pretend
			# the corrupt ones aren't corrupt, since we don't wany any of them
			# anyways.
			if fType == "application/CDFV2-corrupt":
				fType = 'application/CDFV2'

		except magic.MagicException:
			self.tlog.error("REALLY Corrupt Archive! ")
			self.tlog.error("%s", wholePath)
			self.tlog.error("%s", traceback.format_exc())
			fType = "none"
		except IOError:
			self.tlog.error("Something happened to the file before processing (did it get moved?)! ")
			self.tlog.error("%s", wholePath)
			self.tlog.error("%s", traceback.format_exc())
			fType = "none"

		if 	fType == 'application/zip' or \
			fType == 'application/x-rar' or \
			fType == 'application/x-7z-compressed':

			# self.tlog.info("Scanning into archive - %s - %s", fileN, wholePath)

			try:
				self.scanArchive(wholePath, fCont)

			except KeyboardInterrupt:
				raise
			except:
				self.tlog.error("Archive is damaged, corrupt, or not actually an archive: %s", wholePath)
				self.tlog.error("Error Traceback:")
				self.tlog.error(traceback.format_exc())
				# print("wat?")

			# print("Archive scan complete")
			return

	def processFile(self, wholePath):
		if wholePath.startswith("/content"):
			raise ValueError("Wat?")

		# print("path", wholePath)
		if wholePath.lower().endswith(ARCH_EXTS):
			self.processArchive(wholePath)
		else:

			# Get list of all hashes for items on wholePath
			extantItems = self.dbApi.getItemsOnBasePath(wholePath)
			haveFileHashList = [item['itemhash'] != "" for item in extantItems]

			haveAllStatList = [(bool(item['imgx']) and bool(item['imgy'])) for item in extantItems if item['pHash']]

			# Only rescan if we don't have hashes for all the items in the archive (no idea how that would happen),
			# or we have no items for the archive

			if all(haveFileHashList) and len(extantItems) and all(haveAllStatList):

				self.putProgQueue("skipped")
				return

			elif wholePath.lower().endswith(IMAGE_EXTS):  # It looks like an image.

				self.processImageFile(wholePath, wholePath)
				# self.tlog.info("Skipping Image = %s", wholePath)

			# Rehash the overall archive if we don't have a hash-value for the archive with no internalpath.
			elif not any([item['itemhash'] and not item['internalPath'] for item in extantItems]):
				# print("File", wholePath)
				try:
					self.hashBareFile(wholePath, wholePath)

				except IOError:

					self.tlog.error("Something happened to the file before processing (did it get moved?)! ")
					self.tlog.error("%s", wholePath)
					self.tlog.error("%s", traceback.format_exc())
				except (IndexError, UnboundLocalError):
					self.tlog.error("Error while processing wholePath")
					self.tlog.error("%s", wholePath)
					self.tlog.error("%s", traceback.format_exc())

			else:
				self.putProgQueue("skipped")
				# self.tlog.info("Skipping file = %s", wholePath)


import streamlit as st
import langchain_helper as lch
import textwrap

st.title('Youtube Assistant')

with st.sidebar:
    with st.form(key = "my_form"):
        youtube_url = st.sidebar.text_area(
            label = "What is youtube url of your desired video?",
            max_chars = 50
        )
        query = st.sidebar.text_area(
            label = 'Ask me about the video?',
            max_chars = 50,
            key = 'query'

        )

        submit_button = st.form_submit_button(label = 'submit')

if query and youtube_url:
    db = lch.create_vector_db_from_youtube_url(youtube_url)
    response, docs = lch.get_response_from_query(db, query)
    st.subheader("Answer:")
    st.text(textwrap.fill(response, width = 80))


def isSumOfLastInterval(current, interval):
    _min = 0
    _max = len(interval) - 1

    while _min < _max:
        _sum = interval[_min] + interval[_max]
        if current == _sum:
            return True
        elif current > _sum:
            _min += 1
        elif current < _sum:
            _max -= 1

    return False

def insertToInterval(current, interval):
    index = 0
    interval_len = len(interval)
    while index < interval_len and current > interval[index]:
        index += 1

    interval.insert(index, current)

f = open("day9.txt")

numbers = {}
last_interval = []
base = 0
interval = 25

for line in f:
    current = int(line)
    numbers[base] = current

    if base >= interval:
        valid = isSumOfLastInterval(current, last_interval)
        if not valid:
            break

        to_replace = numbers[base - interval]
        index = last_interval.index(to_replace)
        last_interval = last_interval[:index] + last_interval[index+1:]
    base += 1
    insertToInterval(current, last_interval)

magic_number = numbers[base]

data = []
for i in range(base):
    data.append(numbers[i])

start = 0
end = 1

while start < base - 1:
    end = start + 1
    while sum(data[start:end + 1]) < magic_number:
        end += 1

    if sum(data[start:end + 1]) == magic_number:
        break

    start += 1

magic_sum = min(data[start:end + 1]) + max(data[start:end + 1])

print("Part 1: %d is not a sum of any pair of the last %d numbers" % (magic_number, interval))
print("Part 2: %d is the magic number" % (magic_sum))


import HandtrackingModule as htm
import numpy as np
import cv2 as cv
import mediapipe as mp
import os

cap = cv.VideoCapture(0)
hd = htm.handDetector()

# keep track of the points of the tips of our fingers
tipids = [4, 8, 12, 16, 20]

while True:
    isTrue, frame = cap.read()
    #we'll draw our counting over this blank image
    blank = np.zeros((100, 100, 1), dtype='uint8')
    frame, _ = hd.findHands(frame)
    lmlist1 = hd.ListLandmarks(frame)    #takes the landmarks of the points for the first hand
    lmlist2 = hd.ListLandmarks(frame, 1) #takes the landmarks of the second hand
    llmlist = [] #initialize a list that we'll loop over to get both hands through.
    if len(lmlist1) != 0 or len(lmlist2) != 0:  #check to see if the model has found any hands
        if lmlist1[1] == lmlist2[1]:  # this is to prevent the model of giving back the same hand twice
            llmlist.append(lmlist1)
        else:
            llmlist.append(lmlist1)
            llmlist.append(lmlist2)
    fingerups = []   # We'll use this to count our fingers
    for listl in llmlist:    #we initialize the loop over a hand at a time.
        if len(listl) != 0:
            #iterate over the tips of the fingers and check if their distance to the point in the center of the hand is smaller than a threshold
            #if its not we'll append True to the fingerups list.
            for id in tipids:
                point1 = np.array([listl[0][1], listl[0][2]])
                point2 = [listl[id][1], listl[id][2]]
                distance = np.linalg.norm((point1-point2))
                if distance < 120:
                    print('finger closed')

                else:
                    print('finger open')
                    fingerups.append(True)

    #takes the length of the fingerups and draw it over the blank image

    #cv.putText(blank, f'{len(fingerups)}', (37, 60),cv.FONT_HERSHEY_COMPLEX, 1.5, (255, 255, 255), 3)

    #takes the length of the fingerups and draw it on our image
    cv.putText(frame, f'{len(fingerups)}', (frame.shape[0]//6, frame.shape[1]//6),
               cv.FONT_HERSHEY_COMPLEX, 2.5, (255, 0, 0), 3)
    #uses the blank image as a background to our counter.

    #frame[0:100, 0:100] = blank
    cv.imshow('frame', frame)
    if cv.waitKey(2) & 0xff == ord('d'):
        break


def longest_palindrome(string):
    n = len(string)

    matrix = []
    for _ in range(n):
        matrix.append([])
    for i in range(n):
        for j in range(n):
            if i == j:
                matrix[i].append(1)
            else:
                matrix[i].append(0)

    for subseq_len in range(2, n+1):
        n_of_subseq = n - subseq_len + 1
        for i in range(0, n_of_subseq):
            j = i + subseq_len - 1
            if subseq_len == 2 and string[i] == string[j]:
                matrix[i][j] = 2
            elif string[i] == string[j]:
                matrix[i][j] = matrix[i+1][j-1] + 2
            else:
                left = matrix[i+1][j]
                right = matrix[i][j-1]
                matrix[i][j] = max(left, right)
    return matrix[0][n-1]

print(longest_palindrome(input()))


import urllib.request
import yt_dlp

from modules.web_scratch import WebScratch

OUTPUT_LOCATION = './out/'

class YoutubeApi() :
    def __init__(self) :
        self.scratch = WebScratch()

    def search_song(self, song) :
        search_word = song.get_search_word()
        search_url = f'https://www.youtube.com/results?search_query={search_word}'

        video_urls = []
        try :
            print(search_url)
            html = urllib.request.urlopen(search_url)
            html_source = html.read().decode()
            video_urls = self.scratch.gen_video_urls(html_source)
        except UnicodeEncodeError as err:
            return err

        return self.choose_url(video_urls, song)

    def choose_url(self, urls, song) :
        try :
            video_counter = 0
            for url in urls :
                video_counter += 1

                html = urllib.request.urlopen(url)
                html_source = html.read().decode()
                duration = self.scratch.extract_duration(html_source)

                if not duration : #if duration == None
                    continue

                if song.check_duration(duration) :
                    print(f"[YOUTUBE-API] Using video number {video_counter} ( {url} ).")
                    return url

        except Exception as err:
            print(err)

        return urls[0]

    def download(self, urls, name) :
        ydl_ops = {
            'outtmpl': OUTPUT_LOCATION + name,
            'format': 'bestaudio/best',
            'postprocessors': [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': 'mp3',
                'preferredquality': '320',
            }],
        }
        dlp = yt_dlp.YoutubeDL(ydl_ops)
        dlp.download(urls)


from lib import *
from math import pi, tan
from sphere import *
from random import random, randint
from plane import *
from cube import *
from triangle import *
from obj import *

BLACK = color(0, 0, 0)
WHITE = color(255, 255, 255)

MAX_RECURSION_DEPTH = 2

class Raytracer(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.background_color = BLACK
        self.light = None
        self.clear()

    def clear(self):
        self.pixels = [
            [BLACK for _ in range(self.width)]
            for _ in range(self.height)
            ]

    def write(self, filename):
        writeBMP(filename, self.width, self.height, self.pixels)

    def point(self, x, y, col):
        self.pixels[y][x] = col

    def cast_ray(self, origin, direction, recursion = 0):
        material, intersect = self.scene_intersect(origin, direction)

        if material is None or recursion >= MAX_RECURSION_DEPTH:
            return self.background_color

        light_dir = norm(sub(self.light.position, intersect.point))

        offset_normal = mul(intersect.normal, 0.1)
        shadow_orig = sum(intersect.point, offset_normal) \
                       if dot(light_dir, intersect.normal) >= 0 \
                       else sub(intersect.point, offset_normal)

        shadow_material, shadow_intersect = self.scene_intersect(shadow_orig, light_dir)
        if shadow_material is None:
            shadow_intensity = 0
        else:
            shadow_intensity = 0.9

        if material.albedo[2] > 0:
            reverse_direction = mul(direction, -1)
            reflect_direction = reflect(reverse_direction, intersect.normal)
            reflect_orig = sum(intersect.point, offset_normal) \
                       if dot(reflect_direction, intersect.normal) >= 0 \
                       else sub(intersect.point, offset_normal)

            reflect_color = self.cast_ray(reflect_orig, reflect_direction, recursion + 1)
        else:
            reflect_color = color(0, 0, 0)

        if material.albedo[3] > 0:
            refract_direction = refract(direction, intersect.normal, material.refractive_index)
            if refract_direction is None:
                refract_color = color(0, 0, 0)
            else:
                refract_orig = sum(intersect.point, offset_normal) \
                           if dot(reflect_direction, intersect.normal) >= 0 \
                           else sub(intersect.point, offset_normal)

                refract_color = self.cast_ray(refract_orig, refract_direction, recursion + 1)
        else:
            refract_color = color(0, 0, 0)

        diffuse_intensity = self.light.intensity * max(0, dot(light_dir, intersect.normal)) \
        * (1 - shadow_intensity)

        if shadow_intensity > 0:
            specular_intensity = 0
        else:
            specular_reflection = reflect(light_dir, intersect.normal)
            specular_intensity = self.light.intensity * (
                    max(0, dot(specular_reflection, direction)) ** material.spec
                )

        diffuse = material.diffuse * diffuse_intensity * material.albedo[0] #LUZ DIFUSA
        specular = self.light.color * specular_intensity * material.albedo[1] #LUZ ESPECULAR
        reflection =  reflect_color * material.albedo[2] #REFLEXIÓN
        refraction = refract_color * material.albedo[3] #BRILLO DEL COLOR SECUNDARIO

        if material.texture and intersect.text_coords is not None:
            text_color = material.texture.get_color(intersect.text_coords[0], intersect.text_coords[1])
            diffuse = text_color * 255

        c = diffuse + specular + reflection + refraction

        return c

    def scene_intersect(self, origin, direction):
        zbuffer = float('inf')
        material = None
        intersect = None

        for obj in self.scene:
            r_intersect = obj.ray_intersect(origin, direction)

            if r_intersect and r_intersect.distance < zbuffer:
                zbuffer = r_intersect.distance
                material = obj.material
                intersect = r_intersect

        return material, intersect

    def render(self):
        fov = pi/2
        aspect_ratio = self.width/self.height

        for y in range(self.height):
            for x in range(self.width):
                if random() > 0:
                    i = (2 * ((x + 0.5)/self.width) - 1) * aspect_ratio * tan(fov/2)
                    j = 1 - 2 * ((y + 0.5)/self.height) * tan(fov/2)

                    direction = norm(V3(i, j, -1))
                    col = self.cast_ray(V3(0, 0, 0), direction)
                    self.point(x, y, col)

    def Load(self, filename, translate, scale):
        model = Obj(filename)
        triangles_vertex = []

        for face in model.faces:
            f1 = face[0][0] - 1
            f2 = face[1][0] - 1
            f3 = face[2][0] - 1

            A = Transform(model.vertices[f1], translate, scale)
            B = Transform(model.vertices[f2], translate, scale)
            C = Transform(model.vertices[f3], translate, scale)
            triangles_vertex.append([A, B, C])

        return triangles_vertex

r = Raytracer(500, 500)
triangles_vertex = []
scene_objects = []

r.light = Light(
        position=V3(-5, -12, 10),
        intensity=2,
        color=color(255, 255, 200)
    )

#Materiales
moon = Material(diffuse=color(158, 158, 158), albedo=[0.5, 0.3, 0, 0], spec=100)
glass = Material(diffuse=color(0, 208, 216), albedo=[0.1, 8, 0.8, 0.8], spec=1500, refractive_index = 1.5)
redPlastic = Material(diffuse=color(255, 0, 0), albedo=[0.6, 0.3, 0, 0], spec=15)
blackMetal = Material(diffuse=color(35, 35, 34), albedo=[0.6, 0.7, 0.1, 0.1], spec=1500, refractive_index = 0.5)
grass = Material(diffuse=color(57, 125, 16), albedo=[0.6, 0.1, 0, 0], spec=20)
yellowPlastic = Material(diffuse=color(245, 255, 11), albedo=[0.6, 0.3, 0, 0], spec=15)

#Estrellas(Funciona solamente cuando no se renderiza al 100% de definición)
for z in range(0, 499):
    x = randint(0, 499)
    y = randint(0, 499)
    if r.pixels[x][y] == BLACK:
        r.point(x, y, color(255, 245, 51))

#4.5, -14, -15
triangles_vertex = r.Load('./modelos/AU2.obj', (8, -8, -10), (2000, 2000, 2000))

for triangle_vertex in triangles_vertex:
    scene_objects.append(Triangle(triangle_vertex, yellowPlastic))

r.scene = scene_objects

#MOGUS
#PIERNAS
scene_objects.append(Cube(V3(1.5, 2.8, -9), 2, redPlastic))
scene_objects.append(Cube(V3(-1.5, 2.8, -9), 2, redPlastic))

#TORSO
scene_objects.append(Cube(V3(0, 0.5, -12), 6, redPlastic))
scene_objects.append(Cube(V3(0, -2.7, -12), 6, redPlastic))

#VISOR DEL MOGUS
scene_objects.append(Cube(V3(0, -2, -8), 1, blackMetal))
scene_objects.append(Cube(V3(-1, -2, -8), 1, blackMetal))
scene_objects.append(Cube(V3(1, -2, -8), 1, blackMetal))

scene_objects.append(Cube(V3(2, -3, -8), 1, blackMetal))
scene_objects.append(Cube(V3(1, -3, -8), 1, glass))
scene_objects.append(Cube(V3(0, -3, -8), 1, glass))
scene_objects.append(Cube(V3(-1, -3, -8), 1, glass))
scene_objects.append(Cube(V3(-2, -3, -8), 1, blackMetal))

scene_objects.append(Cube(V3(0, -4, -8), 1, blackMetal))
scene_objects.append(Cube(V3(-1, -4, -8), 1, blackMetal))
scene_objects.append(Cube(V3(1, -4, -8), 1, blackMetal))

#GRAMA
scene_objects.append(Cube(V3(-10, 6.7, -14), 10, grass))
scene_objects.append(Cube(V3(0, 6.7, -14), 10, grass))
scene_objects.append(Cube(V3(10, 6.7, -14), 10, grass))

#LUNA
scene_objects.append(Sphere(V3(-8, -7.5, -12), 1, moon))

r.scene = scene_objects
r.render()
r.write('Raytracing.bmp')


import pandas as pd
import re
from ratecard import *
from openpyxl import Workbook
from openpyxl.utils.dataframe import dataframe_to_rows
from openpyxl.styles import PatternFill, Border, Side, Alignment, Protection, Font, Border, Side
from openpyxl.styles import Alignment
from math import ceil

machines = pd.read_csv('TCO reference - C-Machines.csv')
rates = pd.read_csv('TCO reference - O-Rates.csv')

# csv_to_read = input('path to csv file: ')
# df = pd.read_csv(csv_to_read)

#provisional test on Alphasense
df = pd.read_csv('bill_examples/axcient_rowitems.csv')
df = df[df['RecordType']=='PayerLineItem']
#function to detect column names
def detect_column_names(dataframe):
  usage_options = ['lineItem/UsageType', 'UsageType']
  cost_options = ['lineItem/UnblendedCost', 'TotalCost']
  rate_options = ['BlendedRate', 'UnblendedRate', 'lineItem/UnblendedRate']
  description_options = ['lineItem/LineItemDescription', 'ItemDescription']
  quantity_options = ['lineItem/UsageAmount', 'UsageQuantity']
  productname_options = ['product/ProductName', 'ProductCode']
  type_options = ['ItemType']

  columnnames = {
  'usage': list(set(usage_options).intersection(set(dataframe.columns)))[0],
  'cost': list(set(cost_options).intersection(set(dataframe.columns)))[0],
  'rate': list(set(rate_options).intersection(set(dataframe.columns)))[0],
  'description': list(set(description_options).intersection(set(dataframe.columns)))[0],
  'quantity': list(set(quantity_options).intersection(set(dataframe.columns)))[0],
  'productname': list(set(productname_options).intersection(set(dataframe.columns)))[0],
  # 'type': set(type_options).intersection(set(dataframe.columns)))[0],
  }
  return columnnames

#Main component
def sud(nunits, rate):
  n30, remaining = int(nunits)//744, int(nunits)%744
  n15, remaining = remaining//360, remaining%360
  h30, h15, h0 = n30*744, n15*360, remaining
  final = (0.7*h30/nunits + 0.85*h15/nunits + remaining/nunits)*rate
  return final#Returns normalized rate after sud

ratedict = {
    'box': 'List-price',
    'heavy': 'Commitment-1yr',
    'spot': 'Preemptible-use'
}

ssd_rate = {
    'box' : 0.08,
    'heavy' : 0.051,
    'spot' : 0.048
}

gpu_rates = {
    'NVIDIA Tesla K80':{
        'box' : 0.45,
        'heavy' : 0.283,
        'spot' : 0.135
    },
    'NVIDIA Tesla V100':{
        'box' : 2.48,
        'heavy' : 1.562,
        'spot' : 0.74,
    },
    # 'NVIDIA GRID K520':{
    #     'box' : 2.48,
    #     'heavy' : 1.562,
    #     'spot' : 0.74,
    # },
    # 'NVIDIA Tesla M60':{
    #     'box' : 2.48,
    #     'heavy' : 1.562,
    #     'spot' : 0.74,
    # },
    # 'NVIDIA T4 Tensor Core':{
    #     'box' : 2.48,
    #     'heavy' : 1.562,
    #     'spot' : 0.74,
    # }
}

testmode = False

sud_dict = {
    'GPU' : True,
    'All' : True,
}

def get_usagetype(row):

  val = row[columnnames['usage']]

  if 'BoxUsage' in val:
    usagetype = 'box'
  elif 'HeavyUsage' in val:
    usagetype = 'heavy'
  elif 'SpotUsage' in val:
    usagetype = 'spot'
  # print(val)

  if row[columnnames['productname']]=='Amazon Elastic MapReduce':
    usagetype = 'spot'

  return usagetype

def parsecompute(row):
  val = row[columnnames['usage']]
  usagetype = get_usagetype(row)
  try:
    region_aws, rest = val.split('-')
  except:
    region_aws, rest = 'USE1', val
    # return pd.Series([0]*8)
  region_gcp = region_dict[region_aws]
  try:
    usage, aws_machine = rest.split(':')
  except:
    usage, aws_machine = rest, ''
    return pd.Series([0]*8)
  machine = aws_machine.replace('.elasticsearch', '')
  gcpmachine = machines[machines['API Name']==machine].iloc[0]
  gcpmachine_name = [gcpmachine['Family'].upper(), gcpmachine['Type'].capitalize(), gcpmachine['Memory'], gcpmachine['vCPUs']]
  if gcpmachine_name[1]=='Standard' or gcpmachine_name[1]=='Highmem' or gcpmachine_name[1]=='Highcpu':
    gcpmachine_name[1] = 'Predefined'
  #Needs restructuring to accommodate for all regions and pull rate from the new JSON
  if gcpmachine_name[1] not in ['Small', 'Micro', 'Medium']:
    machine_id = '{}-{}'.format(gcpmachine_name[0],gcpmachine_name[1]).lower()
    rates_cpu = compute_ratecard[usagetype]['{}-vcpus'.format(machine_id)][region_gcp]
    rates_mem = compute_ratecard[usagetype]['{}-memory'.format(machine_id)][region_gcp]
    gcp_rate = gcpmachine_name[2]*float(rates_mem)+gcpmachine_name[3]*float(rates_cpu)
    if machine.startswith('p'):
      if gcpmachine['GPU model']=='NVIDIA Tesla K80':
        gcp_rate+=int(gcpmachine['GPUs'])*gpu_rates['NVIDIA Tesla K80'][usagetype]
      elif gcpmachine['GPU model']=='NVIDIA Tesla V100':
        gcp_rate+=int(gcpmachine['GPUs'])*gpu_rates['NVIDIA Tesla V100'][usagetype]
  else:
    gcp_rate = float(rates[(rates['Family']==gcpmachine_name[0])&(rates['Type']==gcpmachine_name[1])].iloc[0][ratedict[usagetype]].replace('$', ''))

  original_rate = gcp_rate
  if row[columnnames['rate']]:
    nunits = row[columnnames['cost']]/row[columnnames['rate']]
    # if usagetype=='box':
    #   gcp_rate = sud(nunits, gcp_rate)
  else:
    nunits = 0
  ssd_cost = 0
  if gcpmachine['Instance Storage']!=0 and gcpmachine['SSD/HDD?']=='SSD':
    ssd_cost +=int(gcpmachine['Instance Storage'])*ssd_rate[usagetype]*(nunits//744)
  sud_savings = nunits*(original_rate-gcp_rate)
  # print(ssd_cost, int(gcpmachine['Instance Storage']), ssd_rate[usagetype], nunits, nunits//744)
  # print(nunits, original_rate)
  returner = [nunits]+gcpmachine_name[:2]+[gcp_rate, nunits*gcp_rate+ssd_cost, sud_savings, ssd_cost, sud_savings/(nunits*original_rate)*100]
  return pd.Series(returner)

columnnames = detect_column_names(df)

# for key in columnnames:
# 	columnnames[key] = input('Enter column name for {}: '.format(key))

df = df[df[columnnames['cost']]>0]
df = df[df[columnnames['usage']]!='']
# df = df[~df[columnnames['description']].str.contains('Total')]

df[columnnames['rate']] = df[columnnames['cost']]/df[columnnames['quantity']]
grouped = df.groupby([columnnames['usage'], columnnames['description'], columnnames['productname']]).agg({columnnames['cost']:'sum', columnnames['rate']:'mean', columnnames['quantity']:'sum'}).reset_index()

aws_actual_cost = sum(grouped[columnnames['cost']])
print(aws_actual_cost)

box_filter = grouped[columnnames['usage']].str.contains('BoxUsage')
boxusage = grouped[box_filter].sort_values(columnnames['usage'])
grouped = grouped[~box_filter]

heavy_filter = (grouped[columnnames['usage']].str.contains('HeavyUsage'))&(~grouped[columnnames['usage']].str.contains('HeavyUsage:db')&(grouped[columnnames['productname']]=='AmazonEC2'))
heavyusage = grouped[heavy_filter].sort_values(columnnames['usage'])
grouped = grouped[~heavy_filter]

spot_filter = grouped[columnnames['usage']].str.contains('SpotUsage')
spotusage = grouped[spot_filter].sort_values(columnnames['usage'])
grouped = grouped[~spot_filter]

if len(boxusage):
  print('Box AWS: ', sum(boxusage[columnnames['cost']]))
  boxusage[['nunits', 'gcp_family', 'gcp_type', 'gcp_rate', 'gcp_cost', 'sud_savings', 'ssd_cost', 'sud_savings_percentage']] = boxusage.apply(parsecompute, axis=1)
  # print('Boxusage', sum(boxusage[columnnames['cost']]), sum(boxusage['gcp_cost']))
if len(heavyusage):
  print('heavy AWS: ', sum(heavyusage[columnnames['cost']]), len(heavyusage))
  heavyusage[['nunits', 'gcp_family', 'gcp_type', 'gcp_rate', 'gcp_cost', 'sud_savings', 'ssd_cost', 'sud_savings_percentage']] = heavyusage.apply(parsecompute, axis=1)
  # print('Heavyusage', sum(heavyusage[columnnames['cost']]), sum(heavyusage['gcp_cost']))
if len(spotusage):
  print('spot AWS: ', sum(spotusage[columnnames['cost']]))
  spotusage[['nunits', 'gcp_family', 'gcp_type', 'gcp_rate', 'gcp_cost', 'sud_savings', 'ssd_cost', 'sud_savings_percentage']] = spotusage.apply(parsecompute, axis=1)
  # print('Spotusage', sum(spotusage[columnnames['cost']]), sum(spotusage['gcp_cost']))

######################################################################################################################################################

pd_filter = (grouped[columnnames['usage']].str.contains('VolumeUsage.gp2')) | (grouped[columnnames['usage']].str.contains('SnapshotUsage')) | ((grouped[columnnames['usage']].str.contains('VolumeUsage'))&(grouped[columnnames['description']].str.contains('Magnetic provisioned storage')))
persistentdisk = grouped[pd_filter].sort_values(columnnames['usage'])
grouped = grouped[~pd_filter]

def parsepd(row):
  value = row[columnnames['usage']]
  # print(value)
  try:
    if not value.startswith('EBS'):
      region, cat = value.split('-EBS:')
    else:
      region, cat = 'USE1', value.replace('EBS:', '')
    if cat=='SnapshotUsage':
      return pd.Series([persistentdisk_ratecard['snapshot'][region_dict[region]], row[columnnames['quantity']]*persistentdisk_ratecard['snapshot'][region_dict[region]]])
    elif cat=='VolumeUsage.gp2':
      return pd.Series([persistentdisk_ratecard['gp2'][region_dict[region]], row[columnnames['quantity']]*persistentdisk_ratecard['gp2'][region_dict[region]]])
    elif 'Magnetic provisioned storage' in row[columnnames['description']]:
      return pd.Series([persistentdisk_ratecard['magnetic'][region_dict[region]], row[columnnames['quantity']]*persistentdisk_ratecard['magnetic'][region_dict[region]]])
  except:
    return pd.Series([0, 0])

persistentdisk[['GCP_rate', 'GCP_cost']] = persistentdisk.apply(parsepd, axis=1)

print("PD AWS: ", sum(persistentdisk[columnnames['cost']]), " PD GCP: ", sum(persistentdisk['GCP_cost']))

######################################################################################################################################################

def nat_gateway_cost(dataframe):
  nat_gateway_bytes = dataframe[dataframe[columnnames['usage']].str.contains('NatGateway-Bytes')].copy()
  nat_gateway_hours = dataframe[dataframe[columnnames['usage']].str.contains('NatGateway-Hours')].copy()
  nat_gateway_hours['ninstances'] = nat_gateway_hours[columnnames['quantity']]/720
  ninstances = sum(nat_gateway_hours['ninstances'])
  ngb = sum(nat_gateway_bytes[columnnames['quantity']])
  if ninstances<=32:
    cost = ninstances*nat_gateway_ratecard['us-vm-low']*720+ngb*nat_gateway_ratecard['us-bytes']
  else:
    cost = nat_gateway_ratecard['us-vm-high']*720+ngb*nat_gateway_ratecard['us-bytes']
  return [cost, sum(nat_gateway_bytes[columnnames['cost']])+sum(nat_gateway_hours[columnnames['cost']])]

nat_gcp, nat_aws = nat_gateway_cost(grouped)#df is the grouped dataframe

nat_filter = grouped[columnnames['usage']].str.contains('NatGateway-Bytes') | grouped[columnnames['usage']].str.contains('NatGateway-Hours')
grouped = grouped[~nat_filter]

#feed this input into the viz tool
######################################################################################################################################################

idle_filter = grouped[columnnames['usage']].str.contains('ElasticIP:IdleAddress')
idleaddress = grouped[idle_filter]
grouped = grouped[~idle_filter]

def idle_addresses_cost(row):
  ninstances = int(row[columnnames['quantity']]/720)
  return pd.Series([ninstances*idle_addresses_ratecard['us']*720, row[columnnames['cost']]])

idleaddress[['gcp_cost', 'aws_cost']] = idleaddress.apply(idle_addresses_cost, axis=1)

######################################################################################################################################################

loadbalancer_filter = ((grouped[columnnames['usage']].str.contains('DataProcessing-Bytes'))|(grouped[columnnames['usage']].str.contains('LCUUsage'))|(grouped[columnnames['usage']].str.contains('LoadBalancerUsage')))&(grouped[columnnames['productname']]=='Amazon Elastic Compute Cloud')
loadbalancer = grouped[loadbalancer_filter]
grouped = grouped[~loadbalancer_filter]

def loadbalancer_cost(row):
  rowsplit = row[columnnames['usage']].split('-')
  region_gcp = region_dict[rowsplit[0]]
  usecase = '-'.join(rowsplit[1:])
  return pd.Series([region_gcp, usecase])

if len(loadbalancer):
  loadbalancer[['region', 'usecase']] = loadbalancer.apply(loadbalancer_cost, axis=1)
  load_grouped = loadbalancer.groupby(['region', 'usecase']).sum().reset_index()[['region', 'usecase', columnnames['quantity']]]

def loadgrouped_cost(row):
  region = row['region']
  usecase = row['usecase']
  quantity = row[columnnames['quantity']]
  if usecase=='LoadBalancerUsage':
    nrules = ceil(quantity/720)
    cost = loadbalancer_ratecard['forwarding_rules'][region]*720
    if nrules>5:
      cost+=(nrules-5)*loadbalancer_ratecard['forwarding_rules_extra'][region]*720
    return pd.Series([nrules, cost])
  else:
    cost = quantity*loadbalancer_ratecard['ingress'][region]
    return pd.Series([quantity, cost])
if len(loadbalancer):
  load_grouped[['rules/gb', 'gcp_cost']] = load_grouped.apply(loadgrouped_cost, axis=1)

######################################################################################################################################################
cloudstorage_filter = (grouped[columnnames['productname']]=='AmazonS3GlacierDeepArchive') | (grouped[columnnames['productname']]=='AmazonS3')
cloudstorage = grouped[cloudstorage_filter]
grouped = grouped[~cloudstorage_filter]

def cloudstorage_cost(row):
  usageval = row[columnnames['usage']]
  desc = row[columnnames['description']]
  quantity = row[columnnames['quantity']]
  if 'Monitoring-Automation' in usageval or 'StorageAnalytics' in usageval or 'TagStorage' in usageval:
    return pd.Series(['','', quantity, 0,0])

  if usageval.endswith('Tier1') or usageval.endswith('Tier4'):
    gcp_sku = 'class-a'
  elif usageval.endswith('Tier2') or usageval.endswith('Tier3'):
    gcp_sku = 'class-b'
  elif 'Retrieval' in usageval:
    gcp_sku = 'retrieval'
  elif 'TimedStorage' in usageval:
    gcp_sku = 'storage'
  elif 'EarlyDelete' in usageval:
    gcp_sku = 'storage'
  else:
    print(row)
    return pd.Series(['','', quantity, 0,row[columnnames['cost']]])
  if 'Glacier Deep Archive' in desc:
    gcp_class = 'archive'
  elif 'Glacier' in desc:
    gcp_class = 'coldline'
  elif 'Infrequent Access' in desc:
    gcp_class = 'nearline'
  else:
    gcp_class = 'standard'

  region_aws = usageval.split('-')[0]
  if region_aws in region_dict:
    region_gcp = region_dict[region_aws]
  else:
    region_gcp = region_dict['USE1']
  gcp_rate = cloudstorage_ratecard['{}-{}'.format(gcp_sku, gcp_class)][region_gcp]
  if gcp_sku in ['class-a', 'class-b']:
    gcp_rate/=10000
  gcp_cost = gcp_rate*quantity
  return pd.Series([gcp_class, gcp_sku, quantity, gcp_rate, gcp_cost])

cloudstorage[['GCP Class', 'GCP SKU', 'Quantity', 'GCP Rate', 'GCP Cost']] = cloudstorage.apply(cloudstorage_cost, axis=1)

cloudstorage_gcp_cost = round(sum(cloudstorage['GCP Cost']), 2)
cloudstorage_aws_cost = round(sum(cloudstorage[columnnames['cost']]), 2)
######################################################################################################################################################

cost_matrix = {
  'aws':{
    'box_compute' : round(sum(boxusage[columnnames['cost']]), 2),
    'heavy_compute' : round(sum(heavyusage[columnnames['cost']]), 2),
    'spot_compute' : round(sum(spotusage[columnnames['cost']]), 2),
    'persistentdisk' : round(sum(persistentdisk[columnnames['cost']]), 2),
    'loadbalancer' : round(sum(loadbalancer[columnnames['cost']]),2),
    'cloudnat' : round(nat_aws, 2),
    'idleaddress' : round(sum(idleaddress['aws_cost']), 2),
  },
  'gcp':{
    'box_compute': 0,
    'heavy_compute' : 0,
    'spot_compute' : 0,
    'persistentdisk' : 0,
    'loadbalancer' : 0,
    'cloudnat' : 0,
    'idleaddress' : 0,

  },
}

if len(boxusage):
  cost_matrix['gcp']['box_compute'] = round(sum(boxusage['gcp_cost']), 2)

if len(heavyusage):
  cost_matrix['gcp']['heavy_compute'] = round(sum(heavyusage['gcp_cost']), 2)

if len(spotusage):
  cost_matrix['gcp']['spot_compute'] = round(sum(spotusage['gcp_cost']), 2)

if len(persistentdisk):
  cost_matrix['gcp']['persistentdisk'] = round(sum(persistentdisk['GCP_cost']), 2)

if len(loadbalancer):
  cost_matrix['gcp']['loadbalancer'] = round(sum(load_grouped['gcp_cost']), 2)

if nat_aws:
  cost_matrix['gcp']['cloudnat'] = round(nat_gcp, 2)

if len(idleaddress):
  cost_matrix['gcp']['idleaddress'] = round(sum(idleaddress['gcp_cost']), 2)

# print(cost_matrix)
######################################################################################################################################################

egress_filter = (grouped[columnnames['usage']].str.contains('DataTransfer'))|(grouped[columnnames['usage']].str.contains('Out-Bytes'))

egress_df = grouped[egress_filter]
egress_df = egress_df.groupby(columnnames['usage']).sum().reset_index()
grouped = grouped[~egress_filter]

egress_df = egress_df[egress_df[columnnames['cost']]!=0]

def gbsplitter(val):
  if val>143360:
    return [10240, 10240*13, val-143360]
  elif val>10240:
    return [10240, val-10240, 0]
  else:
    return [val, 0, 0]

def egress_cost(row):
  # print(row)
  usageval = row[columnnames['usage']]
  quantity = row[columnnames['quantity']]
  # try:
  if 'DataTransfer-Regional-Bytes' in usageval:
    aws_region = usageval.replace('DataTransfer-Regional-Bytes', '')
    if aws_region:
      aws_region = usageval.split('-')[0]
    else:
      aws_region = 'USW2'
    gcp_region = region_dict[aws_region]
    gcp_rate = network_egress_ratecard['vm-vm-internal'][gcp_region.split('-')[0]][gcp_region]
    gcp_cost = quantity*gcp_rate
  elif 'DataTransfer' in usageval:
    aws_region = usageval.replace('DataTransfer-Out-Bytes', '')
    if aws_region:
      aws_region = usageval.split('-')[0]
    else:
      aws_region = 'USW2'
    gcp_region = region_dict[aws_region]
    gcp_rate = network_egress_ratecard['to-worldwide'][gcp_region.split('-')[0]]
    gcp_rate = [gcp_rate["10240"], gcp_rate["143360"], gcp_rate["143361"]]
    gcp_cost = sum(x*y for x,y in zip(gcp_rate, gbsplitter(quantity)))
    gcp_rate = ' '.join(map(str, gcp_rate)) # To let it convert peacefully to excel
  elif 'AWS-Out-Bytes' in usageval:
    # print(row)
    aws_regions = usageval.replace('-AWS-Out-Bytes', '').split('-')
    if len(aws_regions)==2:
      gcp_region_from = region_dict[aws_regions[0]]
      gcp_region_to = region_dict[aws_regions[1]]
      # print(usageval)
      gcp_rate = network_egress_ratecard['vm-vm-external']['{}-{}'.format(gcp_region_from.split('-')[0], gcp_region_to.split('-')[0])][gcp_region_from]
      gcp_cost = gcp_rate*quantity
    else:
      gcp_rate, gcp_cost = 0,0
  return pd.Series([gcp_rate, gcp_cost])
  # except:
  #   return pd.Series([0,0])
egress_df[['gcp_rate', 'gcp_cost']] = egress_df.apply(egress_cost, axis=1)

egress_aws_cost = round(sum(egress_df[columnnames['cost']]), 2)
egress_gcp_cost = round(sum(egress_df['gcp_cost']), 2)
print(egress_gcp_cost, egress_aws_cost)

######################################################################################################################################################
support_filter = grouped[columnnames['usage']]=='Dollar'
support = grouped[support_filter]
grouped = grouped[~support_filter]

######################################################################################################################################################

wb = Workbook()

report_filename = 'tco_report.xlsx'

ws_summary = wb.active
ws_summary.title = 'Summary'

ws_summary.append([''])
ws_summary.append(['', 'Summarized View - Total Cost of Ownership on GCP'])
ws_summary.append(['', 'Type', 'GCP', 'Estimates', 'AWS', 'Actuals', '% Savings'])
center = ws_summary['B2']
center.alignment = Alignment(horizontal='center')
ws_summary.merge_cells(start_row=2, start_column=2, end_row=2, end_column=7)
ws_summary.append(['', 'Compute', 'GCE - Preemptible VMs', '${}'.format(cost_matrix['gcp']['spot_compute']), 'EC2 - Spot Usage', '${}'.format(cost_matrix['aws']['spot_compute'])])
ws_spot = wb.create_sheet(title='SpotUsage')

for column_cells in ws_summary.columns:
  ws_summary.column_dimensions[column_cells[0].column_letter].width = 15

ws_summary.column_dimensions[ws_summary.cell(row=ws_summary._current_row, column=3).column_letter].width = 20
ws_summary.column_dimensions[ws_summary.cell(row=ws_summary._current_row, column=5).column_letter].width = 20

spot_pyxl = dataframe_to_rows(spotusage)
for r_idx, row in enumerate(spot_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_spot.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=SUM(SpotUsage!L:L)'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=SUM(SpotUsage!G:G)'

ws_summary.append(['', 'Compute', 'GCE - Regular VMs(Box)', '${}'.format(cost_matrix['gcp']['box_compute']), 'EC2 - Regular Usage(Box)', '${}'.format(cost_matrix['aws']['box_compute'])])
ws_box = wb.create_sheet(title='BoxUsage')
box_pyxl = dataframe_to_rows(boxusage)
for r_idx, row in enumerate(box_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_box.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=SUM(BoxUsage!L:L)'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=SUM(BoxUsage!G:G)'

ws_summary.append(['', 'Compute', 'GCE - Regular VMs(Heavy)', '${}'.format(cost_matrix['gcp']['heavy_compute']), 'EC2 - Regular Usage(Heavy)', '${}'.format(cost_matrix['aws']['heavy_compute'])])
ws_heavy = wb.create_sheet(title='HeavyUsage')
heavy_pyxl = dataframe_to_rows(heavyusage)
for r_idx, row in enumerate(heavy_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_heavy.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=SUM(HeavyUsage!L:L)'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=SUM(HeavyUsage!G:G)'

ws_summary.append(['', '', '', '${}'.format(cost_matrix['gcp']['box_compute']+cost_matrix['gcp']['heavy_compute']+cost_matrix['gcp']['spot_compute']), '', '${}'.format(cost_matrix['aws']['box_compute']+cost_matrix['aws']['heavy_compute']+cost_matrix['aws']['spot_compute'])])
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=SUM(D5:D6)'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=SUM(F5:F6)'

ws_summary.append(['', 'Storage', 'Persistent Disk', '${}'.format(cost_matrix['gcp']['persistentdisk']), 'Elastic Block Storage', '${}'.format(cost_matrix['aws']['persistentdisk'])])
ws_pd = wb.create_sheet(title='Persistent Disk')
pd_pyxl = dataframe_to_rows(persistentdisk)
for r_idx, row in enumerate(pd_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_pd.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = "=SUM('Persistent Disk'!I:I)"
ws_summary.cell(row=ws_summary._current_row, column=6).value = "=SUM('Persistent Disk'!G:G)"

ws_summary.append(['', 'Storage', 'Cloud Storage', '${}'.format(cloudstorage_gcp_cost), 'Simple Storage Service(S3)', '${}'.format(cloudstorage_aws_cost)])
ws_gcs = wb.create_sheet(title='Cloud Storage')
gcs_pyxl = dataframe_to_rows(cloudstorage)
for r_idx, row in enumerate(gcs_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_gcs.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = "=SUM('Cloud Storage'!L:L)"
ws_summary.cell(row=ws_summary._current_row, column=6).value = "=SUM('Cloud Storage'!G:G)"
# ws_summary.append(['', 'Storage', 'Filestore', '', 'Elastic File Storage', ''])
ws_summary.append(['', '', '', '', '', ''])
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=SUM(D8:D9)'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=SUM(F8:F9)'

ws_summary.append(['', 'Networking', 'Cloud Load Balancer', '${}'.format(cost_matrix['gcp']['loadbalancer']), 'Elastic Load Balancer', '${}'.format(cost_matrix['aws']['loadbalancer'])])
ws_lb = wb.create_sheet(title='Load Balancer')
lb_pyxl = dataframe_to_rows(loadbalancer)
for r_idx, row in enumerate(lb_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_lb.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = "=SUM('Load Balancer'!L:L)"
ws_summary.cell(row=ws_summary._current_row, column=6).value = "=SUM('Load Balancer'!G:G)"

ws_summary.append(['', 'Networking', 'Cloud NAT', round(nat_gcp,2), 'NAT Gateway', round(nat_aws,2)])

ws_summary.append(['', 'Networking', 'Network Egress', '${}'.format(egress_gcp_cost), 'Data Transfer', '${}'.format(egress_aws_cost)])
ws_negress = wb.create_sheet(title='Network Egress')
negress_pyxl = dataframe_to_rows(egress_df)
for r_idx, row in enumerate(negress_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_negress.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = "=SUM('Network Egress'!G:G)"
ws_summary.cell(row=ws_summary._current_row, column=6).value = "=SUM('Network Egress'!E:E)"

ws_summary.append(['', 'Networking', 'Idle Addresseses', '${}'.format(round(sum(idleaddress['gcp_cost']), 2)), 'Idle Addresses', '${}'.format(round(sum(idleaddress['aws_cost']), 2))])
ws_negress = wb.create_sheet(title='Idle Addresses')
Idaddress_pyxl = dataframe_to_rows(idleaddress)
for r_idx, row in enumerate(Idaddress_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_negress.cell(row=r_idx, column=c_idx, value=value)
ws_summary.cell(row=ws_summary._current_row, column=4).value = "=SUM('Idle Addresses'!H:H)"
ws_summary.cell(row=ws_summary._current_row, column=6).value = "=SUM('Idle Addresses'!G:G)"

ws_summary.append(['', '', '', '', '', ''])
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=SUM(D11:D14)'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=SUM(F11:F14)'

ws_summary.append(['', 'DB Services', 'Cloud SQL', '', 'Amazon RDS', ''])
# ws_summary.append(['', 'DB Services', 'Search on GCP', '', 'ElasticSearch', ''])
# ws_summary.append(['', 'DB Services', 'Cache on GCP', '', 'Elasticache', ''])
# ws_summary.append(['', 'DB Services', 'BigQuery', '', 'Redshit', ''])
ws_summary.append(['', '', '', '', '', ''])
ws_summary.append(['', 'Support', 'GCP Support', 0, 'AWS Support Business', sum(support[columnnames['cost']])])

ws_summary.append(['', 'Misc', 'Unclassified', '', 'Misc', ''])
ws_summary.cell(row=ws_summary._current_row, column=4).value = "=SUM('Uncomputed'!G:G)"
ws_summary.cell(row=ws_summary._current_row, column=6).value = "=SUM('Uncomputed'!G:G)"

ws_summary.append(['', '', '', '', '', ''])
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=SUM(D18:D19)'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=SUM(F18:F19)'

ws_summary.append(['', 'GCP Monthly', '', '', 'AWS Monthly', '', ''])
ws_summary.cell(row=ws_summary._current_row, column=4).value = '=D7+D10+D15+D17+D20'
ws_summary.cell(row=ws_summary._current_row, column=6).value = '=F7+F10+F15+F17+F20'

#Throw everything else into the 'Other' tab
border = Border(left=Side(border_style='thin', color='000000'),
                right=Side(border_style='thin', color='000000'),
                top=Side(border_style='thin', color='000000'),
                bottom=Side(border_style='thin', color='000000'))

for i in range(4, 22):
  ws_summary.cell(row=i, column=7).value = "=1-D{}/F{}".format(i, i)
  ws_summary.cell(row=i, column=7).number_format = "0.00%"
  ws_summary.cell(row=i, column=4).number_format = "$0.00"
  ws_summary.cell(row=i, column=6).number_format = "$0.00"
  for col in range(2, 8):
    ws_summary.cell(row=i, column=col).border = border

ws_others = wb.create_sheet(title='Uncomputed')
others_pyxl = dataframe_to_rows(grouped)
for r_idx, row in enumerate(others_pyxl, 1):
    for c_idx, value in enumerate(row, 1):
         ws_others.cell(row=r_idx, column=c_idx, value=value)

# def set_border(ws, cell_range):
#     border = Border(left=Side(border_style='thin', color='000000'),
#                 right=Side(border_style='thin', color='000000'),
#                 top=Side(border_style='thin', color='000000'),
#                 bottom=Side(border_style='thin', color='000000'))

#     rows = ws.iter_rows(cell_range)
#     for row in rows:
#         for cell in row:
#             cell.border = border

# set_border(ws_summary, 'B2:G21')

wb.save(filename=report_filename)

######################################################################################################################################################

print('Remaining ', sum(grouped[columnnames['cost']]))