#!/usr/bin/env python
import csv
import os
#import io
import copy
import pyqrcode
from datetime import datetime #, timedelta
import matplotlib.pyplot as plt
import pandas as pd
from fpdf import FPDF

class PDF(FPDF):
    def header(self):
        # logo
        #self.image('fox_face.png', 10, 8, 25)
        # font
        self.set_font('times', 'B', 20)
        # Title
        self.cell(0, 0, 'Bitpanda Report', ln=True, align = 'C')
        # line break
        self.ln(10)

    def footer(self):
        # set position of the footer
        self.set_y(-15)
        # set font
        self.set_font('times', 'I', 10)
        # Date
        self.cell(0, 10, f'{today.strftime("%d/%m/%Y")}', align = 'L')
        # Page number
        self.cell(0, 10, f'{self.page_no()}/nb', align = 'R')

def removeFiles(gotNames):
    for rows in gotNames:
        if os.path.exists(f"{rows}.png"):
            os.remove(f"{rows}.png")
    if os.path.exists('newTempFile0x123.csv'):
        os.remove('newTempFile0x123.csv')

def generatePieChart(assets_Account, titleName):
    # Pie Chart
    x_list = []
    label_list = []
    for row in range(len(assets_Account)):
        x_list.append(assets_Account[row]["Amount"])
        label_list.append(f'{assets_Account[row]["Asset"]}\n({assets_Account[row]["Amount"]})')
    plt.axis("equal") # Kreisdiagramm rund gestaltet (sonst Standard: oval!)
    plt.pie(x_list, labels=label_list, autopct="%1.1f%%")
    plt.title(titleName)
    plt.savefig(f'{titleName}.png', bbox_inches='tight')
    #print(f'{titleName} generated')
    plt.close()

    return titleName

csv_path_to_file = ''
csv_filename = ''
if csv_path_to_file == '':
    path_input = input("Pfad zur csv-Datei (Drag&Drop, remove ''): ")
    csv_Oldpath = os.path.join(path_input)
else:
    csv_Oldpath = os.path.join(csv_path_to_file, csv_filename)

userKey = 'USER'
os_Name = os.name
if os_Name == 'posix':
    userKey = 'USER'
    csvDf = pd.read_csv(csv_Oldpath, skiprows=6)
elif os_Name == 'nt':
    userKey = 'USERNAME'
    csvDf = pd.read_csv(csv_Oldpath, skiprows=6)
else:
    csvDf = pd.read_csv(csv_Oldpath, skiprows=6)
    userKey = 'USERNAME'
csvDf.to_csv('newTempFile0x123.csv', index=False)
newPath_input = 'newTempFile0x123.csv'
csv_path = os.path.join(newPath_input)
input_file = csv.DictReader(open(csv_path))

gitlink = 'https://github.com/MrRo-de/Bitpanda-Report'

disclaimer_eng = '- Disclaimer: All data is without guarantee, errors and changes are reserved. -'
disclaimer_deu = '- Haftungsausschluss: Alle Angaben ohne Gewähr, Irrtümer und Änderungen vorbehalten. -'

today = datetime.today()
print(f"heute ist der {today}\n")

full_name = input('Dein Namen:\n')
street = input('Straße, Hausnummer:\n')
postcode_city = input('PLZ und Ort:\n')
"""
full_name = ''
street = ''
postcode_city = ''
"""
input_file = csv.DictReader(open(csv_path))
fiat_assets = []
metal_assets = []
crypto_assets = []
stock_assets = []

################################################################################################
#################### Find all Assets [Fiat, Metal, Cryptocurrency, Stocks] #####################
################################################################################################

for row in input_file:
    if row['Asset class'] == 'Fiat':
        fiat_assets.append(row["Asset"])
    elif row['Asset class'] == 'Metal':
        metal_assets.append(row["Asset"])
    elif row['Asset class'] == 'Cryptocurrency':
        crypto_assets.append(row["Asset"])
    elif row['Asset class'] == 'Stock (derivative)':
        stock_assets.append(row["Asset"])

fiat_assets = set(fiat_assets)
metal_assets = set(metal_assets)
crypto_assets = set(crypto_assets)
stock_assets = set(stock_assets)

fiat_list = []
#print(fiat_assets)
fiat_paid = 0.00

for asset in fiat_assets:

    input_file = csv.DictReader(open(csv_path))
    fiat_amount = 0.00

    for row in input_file :
        if asset == row["Asset"]:
            if row["Amount Fiat"] == '-':
                row["Amount Fiat"] = '0.00'
            if row['Fee'] == '-':
                row['Fee'] = '0.00'

            if (row['Transaction Type'] == 'buy'):
                fiat_amount += float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'sell'):
                fiat_amount -= float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'deposit'):
                fiat_amount += float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'withdrawal'):
                fiat_amount -= float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'transfer'):
                if (row['In/Out'] == 'outgoing'):
                    fiat_amount -= float(row["Amount Fiat"])
                elif (row['In/Out'] == 'incoming'):
                    fiat_amount += float(row["Amount Fiat"])

            if (float(row['Fee']) >= 0.00):
                fiat_amount -= float(row['Fee'])

    fiat_amount = '{:.2f}'.format(fiat_amount)
    if float(fiat_amount) > 0.0:
        fiat_list.append({"Asset": asset, "Amount": fiat_amount})

#print(fiat_list)

metal_list = []
#print(metal_assets)
for asset in metal_assets:

    input_file = csv.DictReader(open(csv_path))
    metal_amount = 0.00

    for row in input_file :
        if asset == row["Asset"]:
            if row["Amount Asset"] == '-':
                row["Amount Asset"] = '0.00'
            if row['Fee'] == '-':
                row['Fee'] = '0.00'

            if (row['Transaction Type'] == 'buy'):
                metal_amount += float(row["Amount Asset"])
                fiat_paid -= float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'sell'):
                metal_amount -= float(row["Amount Asset"])
                fiat_paid += float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'deposit'):
                metal_amount += float(row["Amount Asset"])
            elif (row['Transaction Type'] == 'withdrawal'):
                metal_amount -= float(row["Amount Asset"])
            elif (row['Transaction Type'] == 'transfer'):
                metal_amount += float(row["Amount Asset"])

            if (float(row['Fee']) >= 0.00):
                metal_amount -= float(row['Fee'])

    metal_amount = '{:.6f}'.format(metal_amount)
    if float(metal_amount) > 0.000000:
        metal_list.append({"Asset": asset, "Amount": metal_amount})

#print(metal_list)

crypto_list = []
#print(crypto_assets)
for asset in crypto_assets:

    input_file = csv.DictReader(open(csv_path))
    crypto_amount = 0.00

    for row in input_file :
        if asset == row["Asset"]:
            if row["Amount Asset"] == '-':
                row["Amount Asset"] = '0.00'
            if row['Fee'] == '-':
                row['Fee'] = '0.00'

            if (row['Transaction Type'] == 'buy'):
                crypto_amount += float(row["Amount Asset"])
                fiat_paid -= float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'sell'):
                crypto_amount -= float(row["Amount Asset"])
                fiat_paid += float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'deposit'):
                crypto_amount += float(row["Amount Asset"])
            elif (row['Transaction Type'] == 'withdrawal'):
                crypto_amount -= float(row["Amount Asset"])
            elif (row['Transaction Type'] == 'transfer'):
                crypto_amount += float(row["Amount Asset"])

            if (float(row['Fee']) >= 0.00):
                crypto_amount -= float(row['Fee'])

    crypto_amount = '{:.6f}'.format(crypto_amount)
    if float(crypto_amount) > 0.000000:
        crypto_list.append({"Asset": asset, "Amount": crypto_amount})

#print(crypto_list)

stock_list = []
for asset in stock_assets:

    input_file = csv.DictReader(open(csv_path))
    stock_amount = 0.00

    for row in input_file :
        if asset == row["Asset"]:
            if row["Amount Asset"] == '-':
                row["Amount Asset"] = '0.00'
            if row['Fee'] == '-':
                row['Fee'] = '0.00'

            if (row['Transaction Type'] == 'buy'):
                stock_amount += float(row["Amount Asset"])
                fiat_paid -= float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'sell'):
                stock_amount -= float(row["Amount Asset"])
                fiat_paid += float(row["Amount Fiat"])
            elif (row['Transaction Type'] == 'deposit'):
                stock_amount += float(row["Amount Asset"])
            elif (row['Transaction Type'] == 'withdrawal'):
                stock_amount -= float(row["Amount Asset"])
            elif (row['Transaction Type'] == 'transfer'):
                stock_amount += float(row["Amount Asset"])

            if (float(row['Fee']) >= 0.00):
                stock_amount -= float(row['Fee'])

    stock_amount = '{:.6f}'.format(stock_amount)
    if float(crypto_amount) > 0.000000:
        stock_list.append({"Asset": asset, "Amount": stock_amount})

#print(stock_list)
#fiat_list = [{'Asset': 'USD', 'Amount': '985.00'}, {'Asset': 'EUR', 'Amount': '-1465.00'}, {'Asset': 'CHZ', 'Amount': '-0.00'}]
for row in fiat_list:
    if row["Asset"] == 'EUR':
        old = float(row["Amount"])
        calc = old + fiat_paid
        if calc == -0.00:
            calc = 0.00
        if calc <= 0.00:
            calc = 0.00

        row["Amount"] = '{:.2f}'.format(calc)

#print(fiat_list)

################################################################################################
################################ Generate Pie Charts ###########################################
################################################################################################

numberOfCharts = 0
listOfPNG = []

temp_fiat_list = copy.deepcopy(fiat_list)
fiat_list.clear()
for row in temp_fiat_list:
    if abs(float(row['Amount'])) != 0.000000:
        fiat_list.append(row)
#print(fiat_list)
if len(fiat_list) > 0:
    listOfPNG.append(generatePieChart(fiat_list, 'Dein Fiat Portfolio'))
    numberOfCharts += 1

temp_metal_list = copy.deepcopy(metal_list)
metal_list.clear()
for row in temp_metal_list:
    if abs(float(row['Amount'])) != 0.000000:
        metal_list.append(row)
#print(metal_list)
if len(metal_list) > 0:
    listOfPNG.append(generatePieChart(metal_list, 'Dein Metal Portfolio'))
    numberOfCharts += 1

temp_crypto_list = copy.deepcopy(crypto_list)
crypto_list.clear()
for row in temp_crypto_list:
    if abs(float(row['Amount'])) != 0.000000:
        crypto_list.append(row)
#print(crypto_list)
if len(crypto_list) > 0:
    listOfPNG.append(generatePieChart(crypto_list, 'Dein Crypto Portfolio'))
    numberOfCharts += 1

temp_stock_list = copy.deepcopy(stock_list)
stock_list.clear()
for row in temp_stock_list:
    if abs(float(row['Amount'])) != 0.000000:
        stock_list.append(row)
#print(stock_list)
if len(stock_list) > 0:
    listOfPNG.append(generatePieChart(stock_list, 'Dein Stock Portfolio'))
    numberOfCharts += 1

################################################################################################
################################# Get all Transactions #########################################
################################################################################################

input_file = csv.DictReader(open(csv_path))
csvDate_format = '%Y-%m-%d'
csvTime_format = '%H:%M:%S'

fiat_in_dict = []
metal_in_dict = []
crypto_in_dict = []
stock_in_dict = []
fiat_out_dict = []
metal_out_dict = []
crypto_out_dict = []
stock_out_dict = []

temp_fiat_dict = []
temp_metal_dict = []
temp_crypto_dict = []
temp_stock_dict = []

for row in input_file:
    date = datetime.strptime(row['Timestamp'][:10], csvDate_format)
    time = datetime.strptime(row['Timestamp'][11:19], csvTime_format)
    if row['Asset class'] == 'Fiat':
        if (row['Transaction Type'] == 'buy'):
            fiat_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Kauf", "Betrag": row["Amount Fiat"], "Asset": row["Fiat"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'sell'):
            fiat_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Verkauf", "Betrag": row["Amount Fiat"], "Asset": row["Fiat"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'deposit'):
            fiat_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "einzahlen", "Betrag": row["Amount Fiat"], "Asset": row["Fiat"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'withdrawal'):
            fiat_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "auszahlen", "Betrag": row["Amount Fiat"], "Asset": row["Fiat"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'transfer'):
            if (row['In/Out'] == 'outgoing'):
                fiat_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "versenden", "Betrag": row["Amount Fiat"], "Asset": row["Fiat"], "Gebühren": row["Fee"]})
            elif (row['In/Out'] == 'incoming'):
                fiat_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "empfangen", "Betrag": row["Amount Fiat"], "Asset": row["Fiat"], "Gebühren": row["Fee"]})
    elif row['Asset class'] == 'Metal':
        if (row['Transaction Type'] == 'buy'):
            metal_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Kauf", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'sell'):
            metal_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Verkauf", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'deposit'):
            metal_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "einzahlen", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'withdrawal'):
            metal_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "auszahlen", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'transfer'):
            metal_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "erhalten", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
    elif row['Asset class'] == 'Cryptocurrency':
        if (row['Transaction Type'] == 'buy'):
            crypto_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Kauf", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'sell'):
            crypto_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Verkauf", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'deposit'):
            crypto_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "empfangen", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'withdrawal'):
            crypto_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "versenden", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'transfer'):
            if row['Asset'] == 'BEST':
                crypto_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Rewards", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
            else:
                crypto_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "erhalten", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
    elif row['Asset class'] == 'Stock (derivative)':
        if (row['Transaction Type'] == 'buy'):
            stock_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Kauf", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'sell'):
            stock_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Verkauf", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'deposit'):
            stock_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "empfangen", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'withdrawal'):
            stock_out_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "versenden", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
        elif (row['Transaction Type'] == 'transfer'):
            if row['Asset'] == 'BEST':  # Ist integriert für den Fall von Dividenden / Muss jedoch angepasst werden.
                stock_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "Rewards", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})
            else:
                stock_in_dict.append({"Datum": f'{date.date()} {time.time()}', "Transaktion": "erhalten", "Betrag": row["Amount Fiat"], "Asset Menge": row["Amount Asset"], "Asset Preis": row["Asset market price"], "Asset": row["Asset"], "Gebühren": row["Fee"]})

fiat_in_dict = sorted(fiat_in_dict, key=lambda k: k['Datum'])
fiat_out_dict = sorted(fiat_out_dict, key=lambda k: k['Datum'])
metal_in_dict = sorted(metal_in_dict, key=lambda k: k['Datum'])
metal_out_dict = sorted(metal_out_dict, key=lambda k: k['Datum'])
crypto_in_dict = sorted(crypto_in_dict, key=lambda k: k['Datum'])
crypto_out_dict = sorted(crypto_out_dict, key=lambda k: k['Datum'])
stock_in_dict = sorted(stock_in_dict, key=lambda k: k['Datum'])
stock_out_dict = sorted(stock_out_dict, key=lambda k: k['Datum'])

temp_fiat_dict = fiat_in_dict + fiat_out_dict
temp_metal_dict = metal_in_dict + metal_out_dict
temp_crypto_dict = crypto_in_dict + crypto_out_dict
temp_stock_dict = stock_in_dict + stock_out_dict

temp_fiat_dict = sorted(temp_fiat_dict, key=lambda k: k['Datum'])
temp_metal_dict = sorted(temp_metal_dict, key=lambda k: k['Datum'])
temp_crypto_dict = sorted(temp_crypto_dict, key=lambda k: k['Datum'])
temp_stock_dict = sorted(temp_stock_dict, key=lambda k: k['Datum'])

################################################################################################
############################### Calculate Win and Losses #######################################
################################################################################################

metal_winloss = []
temp_metal_steuern = []
metal_steuern = []
metal_portfolio = []
crypto_winloss = []
temp_crypto_steuern = []
crypto_steuern = []
crypto_portfolio = []
stock_winloss = []
temp_stock_steuern = []
stock_steuern = []
stock_portfolio = []
steuern = []
steuernCalc = []

#{"Asset": asset, "HODL": days.days, "Jahr": year.year, "winLoss": '{:.2f}'.format(temp_win_loss)}
def calcMetalSteuern(asset, winloss):
    temp_year = datetime.strptime('1990-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
    temp_toPay = 0.00
    temp_Asset = ''
    for row in winloss:
        if row['Asset'][-1] == '*':
            temp_Asset = row['Asset'][:-1]
        else:
            temp_Asset = row['Asset']
        if asset == temp_Asset:
            if int(row['HODL']) <= 365:
                if row['Jahr'] == temp_year:
                    temp_toPay += float(row['winLoss'])
                else:
                    if temp_toPay != 0.00:
                        steuernCalc.append({"Asset": row['Asset'], "verkaufs Jahr": temp_year, "Betrag": temp_toPay})
                    temp_toPay = float(row['winLoss'])
                    temp_year = row['Jahr']

    if temp_toPay != 0.00:
        steuernCalc.append({"Asset": row['Asset'], "verkaufs Jahr": temp_year, "Betrag": temp_toPay})
    return steuernCalc

def calcCryptoSteuern(asset, winloss):
    temp_year = datetime.strptime('1990-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
    temp_toPay = 0.00
    temp_Asset = ''
    for row in winloss:
        if row['Asset'][-1] == '*':
            temp_Asset = row['Asset'][:-1]
        else:
            temp_Asset = row['Asset']
        if asset == temp_Asset:
            if int(row['HODL']) <= 365:
                if row['Jahr'] == temp_year:
                    temp_toPay += float(row['winLoss'])
                else:
                    if temp_toPay != 0.00:
                        steuernCalc.append({"Asset": row['Asset'], "verkaufs Jahr": temp_year, "Betrag": temp_toPay})
                    temp_toPay = float(row['winLoss'])
                    temp_year = row['Jahr']

    if temp_toPay != 0.00:
        steuernCalc.append({"Asset": row['Asset'], "verkaufs Jahr": temp_year, "Betrag": temp_toPay})
    return steuernCalc

def calcStockSteuern(asset, winloss):
    temp_year = datetime.strptime('1990-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')
    temp_toPay = 0.00
    temp_Asset = ''
    for row in winloss:
        if row['Asset'][-1] == '*':
            temp_Asset = row['Asset'][:-1]
        else:
            temp_Asset = row['Asset']
        if asset == temp_Asset:
            if row['Jahr'] == temp_year:
                temp_toPay += float(row['winLoss'])
            else:
                if temp_toPay != 0.00:
                    steuernCalc.append({"Asset": row['Asset'], "verkaufs Jahr": temp_year, "Betrag": temp_toPay})
                temp_toPay = float(row['winLoss'])
                temp_year = row['Jahr']

    if temp_toPay != 0.00:
        steuernCalc.append({"Asset": row['Asset'], "verkaufs Jahr": temp_year, "Betrag": temp_toPay})
    return steuernCalc

def calcWinLoss(in_dict, assets, out_dict, asset_class):

    test = ''
    winloss = []
    steuern_winloss = []
    steuern_metal = []
    steuern_crypto = []
    steuern_stock = []
    temp_winloss = []
    portfolio_dict = []
    for asset in assets:
        hint = False
        temp_in_dict = []
        for row_in in in_dict:
            if row_in['Asset'] == asset:
                if row_in['Betrag'] == '-':
                    row_in['Betrag'] = 0.00
                if row_in['Gebühren'] == '-':
                    row_in['Gebühren'] = 0.00
                if row_in["Transaktion"] == 'empfangen':
                    hint = True
                temp_in_dict.append({"Datum": row_in['Datum'], "Transaktion": row_in['Transaktion'], "Betrag": '{:.2f}'.format(float(row_in["Betrag"])), "Asset Menge": '{:.6f}'.format(float(row_in["Asset Menge"])), "Asset Preis": '{:.2f}'.format(float(row_in["Asset Preis"])), "Asset": row_in["Asset"], "Gebühren": '{:.6f}'.format(float(row_in["Gebühren"]))})

        temp_in_dict = sorted(temp_in_dict, key=lambda k: k['Datum'])

        temp_out_dict = []
        for row_out in out_dict:
            if row_out['Asset'] == asset:
                if row_out['Betrag'] == '-':
                    row_out['Betrag'] = 0.00
                if row_out['Gebühren'] == '-':
                    row_out['Gebühren'] = 0.00
                temp_out_dict.append({"Datum": row_out['Datum'], "Transaktion": row_out['Transaktion'], "Betrag": '{:.2f}'.format(float(row_out["Betrag"])), "Asset Menge": '{:.6f}'.format(float(row_out["Asset Menge"])), "Asset Preis": '{:.2f}'.format(float(row_out["Asset Preis"])), "Asset": row_out["Asset"], "Gebühren": '{:.6f}'.format(float(row_out["Gebühren"]))})

        temp_out_dict = sorted(temp_out_dict, key=lambda k: k['Datum'])

        win_loss = 0.00
        temp_win_loss = 0.00
        temp2_win_loss = 0.00
        #index = 0
        for row_out in temp_out_dict:
            temp_win_loss = 0.00
            if row_out['Asset'] == asset:
                days = datetime.strptime(row_out["Datum"], '%Y-%m-%d %H:%M:%S')-datetime.strptime(temp_in_dict[0]["Datum"], '%Y-%m-%d %H:%M:%S')
                year = datetime.strptime(row_out['Datum'], '%Y-%m-%d %H:%M:%S')
                if float(row_out['Asset Menge']) == 0.000000:
                    row_out['Asset Menge'] = row_out['Gebühren']

                if len(temp_in_dict) > 0:
                    if float(row_out['Asset Menge']) > float(temp_in_dict[0]['Asset Menge']):
                        temp2_win_loss = 0.00
                        while float(row_out['Asset Menge']) > float(temp_in_dict[0]['Asset Menge']):
                            #print(f'1: {len(temp_in_dict)}')
                            if row_out['Transaktion'] == 'Verkauf':
                                temp2_win_loss += (float(temp_in_dict[0]['Asset Menge']) * float(row_out['Asset Preis'])) - float(temp_in_dict[0]['Betrag'])
                            row_out['Betrag'] = '{:.2f}'.format((float(row_out['Asset Menge']) - float(temp_in_dict[0]['Asset Menge'])) * float(row_out['Asset Preis']))
                            row_out['Asset Menge'] = '{:.6f}'.format(float(row_out['Asset Menge']) - float(temp_in_dict[0]['Asset Menge']))

                            if len(temp_in_dict) > 0:
                                del temp_in_dict[0]
                            else:
                                temp_in_dict[0]['Betrag'] = 0.00
                                temp_in_dict[0]['Asset Menge'] = 0.000000

                            if float(row_out['Asset Menge']) < 0.00000:
                                #print('break because of Amount')
                                break
                            #print(f'2: {len(temp_in_dict)}')
                            if len(temp_in_dict) == 0:
                                #print('break because of length')
                                break

                        temp_win_loss += temp2_win_loss

                    if len(temp_in_dict) > 0:
                        if float(row_out['Asset Menge']) == float(temp_in_dict[0]['Asset Menge']):
                            if row_out['Transaktion'] == 'Verkauf':
                                temp_win_loss = temp_win_loss + (float(row_out['Betrag']) - float(temp_in_dict[0]['Betrag']))
                            if len(temp_in_dict) > 0:
                                del temp_in_dict[0]
                            else:
                                temp_in_dict[0]['Betrag'] = 0.00
                                temp_in_dict[0]['Asset Menge'] = 0.000000
                        elif float(row_out['Asset Menge']) < float(temp_in_dict[0]['Asset Menge']):
                            temp_in_dict[0]['Betrag'] = '{:.2f}'.format((float(temp_in_dict[0]['Asset Menge']) - float(row_out['Asset Menge'])) * float(temp_in_dict[0]['Asset Preis']))
                            temp_in_dict[0]['Asset Menge'] = '{:.6f}'.format((float(temp_in_dict[0]['Asset Menge']) - float(row_out['Asset Menge'])))
                            if row_out['Transaktion'] == 'Verkauf':
                                temp_win_loss = temp_win_loss + (float(row_out['Betrag']) - (float(temp_in_dict[0]['Asset Preis']) * float(row_out['Asset Menge'])))

            win_loss += temp_win_loss
            if hint == True:
                steuern_winloss.append({"Asset": f'{asset}*', "HODL": days.days, "Jahr": year.year, "winLoss": '{:.2f}'.format(temp_win_loss)})
                #hint = False
            else:
                steuern_winloss.append({"Asset": asset, "HODL": days.days, "Jahr": year.year, "winLoss": '{:.2f}'.format(temp_win_loss)})

            if asset == test:
                print(win_loss)
                print(temp_win_loss)
        temp_win_loss = 0.00
        temp_winloss.clear()

        if len(temp_in_dict) > 0:
            for row in temp_in_dict:
                if float(row['Asset Menge']) > 0.0:
                    portfolio_dict.append(row)

        winloss.append({"Asset": asset, "winLoss": '{:.2f}'.format(win_loss)})

        if win_loss != 0.00:
            if asset_class == 'metal':
                steuern_metal = calcMetalSteuern(asset, steuern_winloss)
            elif asset_class == 'crypto':
                steuern_crypto = calcCryptoSteuern(asset, steuern_winloss)
            elif asset_class == 'stock':
                steuern_stock = calcStockSteuern(asset, steuern_winloss)
        else:
            if asset_class == 'metal':
                steuern_metal.append({"Asset": asset, "verkaufs Jahr": 1990, "Betrag": 0.00})
            elif asset_class == 'crypto':
                steuern_crypto.append({"Asset": asset, "verkaufs Jahr": 1990, "Betrag": 0.00})
            elif asset_class == 'stock':
                steuern_stock.append({"Asset": asset, "verkaufs Jahr": 1990, "Betrag": 0.00})

    if asset_class == 'metal':
        return winloss, steuern_metal, portfolio_dict
    if asset_class == 'crypto':
        return winloss, steuern_crypto, portfolio_dict
    if asset_class == 'stock':
        return winloss, steuern_stock, portfolio_dict

################################################################################################
################################## Generate PDF File ###########################################
################################################################################################

# create FPDF object
# Layout ('P', 'L')
# Unit ('mm', 'cm', 'in')
# Format ('A3', 'A4' (default), 'A5', 'Letter', 'Legal', (100,150))
pdf = PDF('P', 'mm')
pdf_width = 210
pdf_height = 297
# get total page numbers
pdf.alias_nb_pages(alias='nb')

# Add a Page
pdf.add_page()

# specify font
# fonts ('times', 'courier', 'helvetica', 'symbol', 'zpfdingbats')
# 'B' (bold), 'U' (underline), 'I' (italic), '' (regular), combination (i.e. ('BU'))
pdf.set_font('times', '', 14)

# Set auto page break
pdf.set_auto_page_break(auto=True, margin = 15)

################################################################################################
##################################### First PDF Page ###########################################
################################################################################################

fiat_assets = sorted(fiat_assets, key=lambda k: k)
metal_assets = sorted(metal_assets, key=lambda k: k)
crypto_assets = sorted(crypto_assets, key=lambda k: k)
stock_assets = sorted(stock_assets, key=lambda k: k)
'''
if os.path.exists("Dein Fiat Portfolio.png"):
    print('Fiat PNG exists')
if os.path.exists("Dein Crypto Portfolio.png"):
    print('Crypto PNG exists')
if os.path.exists("Dein Metal Portfolio.png"):
    print('Metal PNG exists')
if os.path.exists("Dein Stock Portfolio.png"):
    print('Stock PNG exists')
'''
# Add text
# w = width
# h = height
# txt = your text
# ln (0 False; 1 True - move cursor down to next line)
# border (0 False; 1 True - add border around cell)
pdf.ln(5)
pdf.cell(40, 8, f"{full_name}", ln=True, border=False)
pdf.cell(40, 8, f"{street}", ln=True)
pdf.cell(40, 8, f"{postcode_city}", ln=True)
pdf.ln(6)
pdf.set_font('times', 'B', 10)
pdf.cell(40, 8, f"Du hast gehandelt mit:\n", ln=True)
pdf.ln(3)
pdf.set_font('times', 'B', 10)
pdf.cell(40, 8, "Fiat\n", ln=True)
pdf.set_font('times', '', 10)
pdf.cell(40, 8, f"{fiat_assets}\n", ln=True)
pdf.set_font('times', 'B', 10)
pdf.cell(40, 8, "Metal\n", ln=True)
pdf.set_font('times', '', 10)
pdf.cell(40, 8, f"{metal_assets}\n", ln=True)
pdf.set_font('times', 'B', 10)
pdf.cell(40, 8, "Crypto\n", ln=True)
pdf.set_font('times', '', 10)
pdf.cell(40, 8, f"{crypto_assets}\n", ln=True)
pdf.set_font('times', 'B', 10)
pdf.cell(40, 8, "Aktien\n", ln=True)
pdf.set_font('times', '', 10)
pdf.cell(40, 8, f"{stock_assets}\n", ln=True)
if numberOfCharts == 4:
    if os.path.exists("Dein Fiat Portfolio.png"):
        pdf.image('Dein Fiat Portfolio.png', pdf_width-85, 140, 70)
    if os.path.exists("Dein Metal Portfolio.png"):
        pdf.image('Dein Metal Portfolio.png', 15, 200, 70)
    if os.path.exists("Dein Crypto Portfolio.png"):
        pdf.image('Dein Crypto Portfolio.png', 15, 140, 70)
    if os.path.exists("Dein Stock Portfolio.png"):
        pdf.image('Dein Stock Portfolio.png', pdf_width-85, 200, 70)
elif numberOfCharts == 3:
    count = 0
    if os.path.exists("Dein Fiat Portfolio.png"):
        if count == 0:
            pdf.image('Dein Fiat Portfolio.png', 15, 140, 70)
        elif count ==1:
            pdf.image('Dein Fiat Portfolio.png', pdf_width-85, 140, 70)
        else:
            pdf.image('Dein Fiat Portfolio.png', (pdf_width/2)-35, 200, 70)
        count += 1
    if os.path.exists("Dein Metal Portfolio.png"):
        if count == 0:
            pdf.image('Dein Metal Portfolio.png', 15, 140, 70)
        elif count == 1:
            pdf.image('Dein Metal Portfolio.png', pdf_width-85, 140, 70)
        else:
            pdf.image('Dein Metal Portfolio.png', (pdf_width/2)-35, 200, 70)
        count += 1
    if os.path.exists("Dein Crypto Portfolio.png"):
        if count == 0:
            pdf.image('Dein Crypto Portfolio.png', 15, 140, 70)
        elif count == 1:
            pdf.image('Dein Crypto Portfolio.png', pdf_width-85, 140, 70)
        else:
            pdf.image('Dein Crypto Portfolio.png', (pdf_width/2)-35, 200, 70)
        count += 1
    if os.path.exists("Dein Stock Portfolio.png"):
        if count == 0:
            pdf.image('Dein Stock Portfolio.png', 15, 140, 70)
        elif count == 1:
            pdf.image('Dein Stock Portfolio.png', pdf_width-85, 140, 70)
        else:
            pdf.image('Dein Stock Portfolio.png', (pdf_width/2)-35, 200, 70)
        count += 1
elif numberOfCharts == 2:
    count = 0
    if os.path.exists("Dein Fiat Portfolio.png"):
        if count == 0:
            pdf.image('Dein Fiat Portfolio.png', 15, 140, 100)
        else:
            pdf.image('Dein Fiat Portfolio.png', pdf_width-115, 140, 100)
        count += 1
    if os.path.exists("Dein Metal Portfolio.png"):
        if count == 0:
            pdf.image('Dein Metal Portfolio.png', 15, 140, 100)
        else:
            pdf.image('Dein Metal Portfolio.png', pdf_width-115, 140, 100)
        count += 1
    if os.path.exists("Dein Crypto Portfolio.png"):
        if count == 0:
            pdf.image('Dein Crypto Portfolio.png', 15, 140, 100)
        else:
            pdf.image('Dein Crypto Portfolio.png', pdf_width-115, 140, 100)
        count += 1
    if os.path.exists("Dein Stock Portfolio.png"):
        if count == 0:
            pdf.image('Dein Stock Portfolio.png', 15, 140, 100)
        else:
            pdf.image('Dein Stock Portfolio.png', pdf_width-115, 140, 100)
        count += 1
elif numberOfCharts == 1:
    if os.path.exists("Dein Fiat Portfolio.png"):
        pdf.image('Dein Fiat Portfolio.png', (pdf_width/2)-75, 140, 150)
    if os.path.exists("Dein Metal Portfolio.png"):
        pdf.image('Dein Metal Portfolio.png', (pdf_width/2)-75, 140, 150)
    if os.path.exists("Dein Crypto Portfolio.png"):
        pdf.image('Dein Crypto Portfolio.png', (pdf_width/2)-75, 140, 150)
    if os.path.exists("Dein Stock Portfolio.png"):
        pdf.image('Dein Stock Portfolio.png', (pdf_width/2)-75, 140, 150)

pdf.ln(140)
pdf.set_font('times', 'I', 12)
pdf.cell(0, 0, disclaimer_deu, ln=True, align="C")
pdf.ln(5)
pdf.set_font('times', '', 10)
pdf.cell(0, 0, disclaimer_eng, ln=True, align="C")
pdf.ln(5)
pdf.set_font('times', '', 10)

################################################################################################
############################# Detailed Transaction Pages #######################################
################################################################################################

def generateFiatTransactionPages(temp_dict_sorted, asset_set, asset_class):

    asset_set = sorted(asset_set, key=lambda k: k)

    for asset in asset_set:

        pdf.add_page()
        pdf.set_font('times', 'B', 10)
        pdf.cell(40, 8, f"Details der {asset} {asset_class} Transaktionen:\n", ln=True)
        pdf.set_font('times', '', 10)
        pdf.ln(5)
        col_width = (pdf_width-30)/4
        pdf.set_font('times', 'B', 10)
        th = pdf.font_size+2
        pdf.cell(col_width, th, "Datum", align='C', border=1)
        pdf.cell(col_width, th, "Transaktion", align='C', border=1)
        pdf.cell(col_width, th, "Betrag", align='C', border=1)
        pdf.cell(col_width, th, "Gebühren", align='C', border=1)
        pdf.ln(th)
        pdf.set_font('times', '', 9)
        th = pdf.font_size+2
        for row in temp_dict_sorted:
            if row['Asset'] == asset:
                pdf.cell(col_width, th, str(row["Datum"]), border=1)
                pdf.cell(col_width, th, str(row["Transaktion"]), border=1)
                pdf.cell(col_width, th, str(row["Betrag"]), border=1)
                pdf.cell(col_width, th, str(row["Gebühren"]), border=1)
                pdf.ln(th)

def generateTransactionPages(temp_dict_sorted, asset_set, winloss, asset_class):

    for asset in asset_set:
        hint = False
        pdf.add_page()
        pdf.set_font('times', 'B', 10)
        pdf.cell(40, 8, f"Details der {asset} {asset_class} Transaktionen:\n", ln=True)
        pdf.set_font('times', '', 10)
        pdf.ln(5)
        col_width = (pdf_width-30)/6
        pdf.set_font('times', 'B', 10)
        th = pdf.font_size+2
        pdf.cell(col_width, th, "Datum", align='C', border=1)
        pdf.cell(col_width, th, "Transaktion", align='C', border=1)
        pdf.cell(col_width, th, "Betrag", align='C', border=1)
        pdf.cell(col_width, th, "Asset Menge", align='C', border=1)
        pdf.cell(col_width, th, "Asset Preis", align='C', border=1)
        pdf.cell(col_width, th, "Gebühren", align='C', border=1)
        pdf.ln(th)
        pdf.set_font('times', '', 9)
        th = pdf.font_size+2
        for row in temp_dict_sorted:
            if row['Asset'] == asset:
                if row["Transaktion"] == 'empfangen':
                    hint = True
                pdf.cell(col_width, th, str(row["Datum"]), border=1)
                pdf.cell(col_width, th, str(row["Transaktion"]), border=1)
                pdf.cell(col_width, th, str(row["Betrag"]), border=1)
                pdf.cell(col_width, th, str(row["Asset Menge"]), border=1)
                pdf.cell(col_width, th, str(row["Asset Preis"]), border=1)
                pdf.cell(col_width, th, str(row["Gebühren"]), border=1)
                pdf.ln(th)

        for rows in winloss:
            if rows['Asset'] == asset:
                pdf.ln(th*2)
                pdf.set_font('times', 'B', 10)
                pdf.cell(45, th, f"Gewinn/Verlust: ")
                if float(rows['winLoss']) < 0.0:
                    pdf.set_text_color(225, 0, 0)
                elif float(rows['winLoss']) > 0.0:
                    pdf.set_text_color(0, 225, 0)
                else:
                    pdf.set_text_color(0, 0, 0)
                pdf.cell(100, th,f" {'{:.2f}'.format(float(rows['winLoss']))} EUR", ln=True)
                pdf.set_font('times', '', 10)
                pdf.set_text_color(0, 0, 0)

        if hint == True:
            pdf.set_text_color(225, 0, 0)
            pdf.ln(th)
            pdf.cell(45, th, f"* Durch das Einzahlen des Assets ist eine genaue Berechnung nicht möglich.")
            pdf.set_text_color(0, 0, 0)
            hint = False

temp_metal_in_dict = copy.deepcopy(metal_in_dict)
temp_metal_out_dict = copy.deepcopy(metal_out_dict)
temp_metal_assets = copy.deepcopy(metal_assets)

temp_crypto_in_dict = copy.deepcopy(crypto_in_dict)
temp_crypto_out_dict = copy.deepcopy(crypto_out_dict)
temp_crypto_assets = copy.deepcopy(crypto_assets)

temp_stock_in_dict = copy.deepcopy(stock_in_dict)
temp_stock_out_dict = copy.deepcopy(stock_out_dict)
temp_stock_assets = copy.deepcopy(stock_assets)

steuernCalc.clear()
metal_winloss, temp_metal_steuern, metal_portfolio = calcWinLoss(temp_metal_in_dict, temp_metal_assets, temp_metal_out_dict, 'metal')
metal_steuern = copy.deepcopy(temp_metal_steuern)

steuernCalc.clear()
crypto_winloss, temp_crypto_steuern, crypto_portfolio = calcWinLoss(temp_crypto_in_dict, temp_crypto_assets, temp_crypto_out_dict, 'crypto')
crypto_steuern = copy.deepcopy(temp_crypto_steuern)

steuernCalc.clear()
stock_winloss, temp_stock_steuern, stock_portfolio = calcWinLoss(temp_stock_in_dict, temp_stock_assets, temp_stock_out_dict, 'stock')
stock_steuern = copy.deepcopy(temp_stock_steuern)

generateFiatTransactionPages(temp_fiat_dict, fiat_assets, 'Fiat')
generateTransactionPages(temp_metal_dict, metal_assets, metal_winloss, 'Metal')
generateTransactionPages(temp_crypto_dict, crypto_assets, crypto_winloss, 'Crypto')
generateTransactionPages(temp_stock_dict, stock_assets, stock_winloss, 'Aktien')

################################################################################################
################################## Steuern Deutschland #########################################
################################################################################################

def generateTaxPage(steuern, asset_class):

    pdf.add_page()
    pdf.ln(5)
    pdf.set_font('times', 'B', 10)
    pdf.cell(40, 8,f"Welchen Gewinn muss ich Versteuern:", ln=True)
    pdf.set_font('times', '', 10)
    pdf.ln(3)
    if asset_class == 'stock':
        pdf.cell(0, 0,f"Für Aktien in Deutschland (Jede Veräußerung muss versteuert werden)", ln=True, align='C')
    elif asset_class == 'metal':
        pdf.cell(0, 0,f"Für Edelmetalle in Deutschland (Steuerfrei bei Haltefrist länger als 1 Jahr)", ln=True, align='C')
    elif asset_class == 'crypto':
        pdf.cell(0, 0,f"Für Cryptowährungen in Deutschland (Steuerfrei bei Haltefrist länger als 1 Jahr)", ln=True, align='C')
    pdf.ln(12)
    col_width = (pdf_width-30)/3
    th = pdf.font_size+2
    pdf.set_font('times', 'B', 10)
    pdf.cell(col_width, th, "Asset", align='C', border=1)
    pdf.cell(col_width, th, "relevantes Jahr", align='C', border=1)
    pdf.cell(col_width, th, "Betrag", align='C', border=1)
    pdf.ln(th)
    pdf.set_font('times', '', 9)
    for row in steuern:
        pdf.cell(col_width, th, row["Asset"], border=1)
        pdf.cell(col_width, th, str(row["verkaufs Jahr"]), align='C', border=1)
        if float(row["Betrag"]) < 0.0:
            pdf.set_text_color(225, 0, 0)
        elif float(row["Betrag"]) > 0.0:
            pdf.set_text_color(0, 225, 0)
        pdf.cell(col_width, th, '{:.2f}'.format(row["Betrag"]), align='C', border=1)
        pdf.set_text_color(0, 0, 0)
        pdf.ln(th)

    pdf.cell(45, th, f"* Durch das Einzahlen des Assets ist eine genaue Berechnung nicht möglich.")
    pdf.ln(16)

    temp_year = steuern[0]["verkaufs Jahr"]
    zahlendeSteuern = 0.00
    for row in steuern:
        if temp_year == row["verkaufs Jahr"]:
            zahlendeSteuern += float(row["Betrag"])
        else:
            if temp_year == today.year:
                pdf.cell(45, th, f"In {temp_year} sind bis jetzt Gewinne/Verluste im Wert von {'{:.2f}'.format(zahlendeSteuern)} angefallen.")
                pdf.ln(6)
            else:
                pdf.cell(45, th, f"In {temp_year} sind Gewinne/Verluste im Wert von {'{:.2f}'.format(zahlendeSteuern)} angefallen.")
                pdf.ln(6)
            zahlendeSteuern = float(row["Betrag"])
            temp_year = row["verkaufs Jahr"]

    if zahlendeSteuern != 0.00:
        if temp_year == today.year:
            pdf.cell(45, th, f"In {temp_year} sind bis jetzt Gewinne/Verluste im Wert von {'{:.2f}'.format(zahlendeSteuern)} angefallen.")
            pdf.ln(6)
        else:
            pdf.cell(45, th, f"In {temp_year} sind Gewinne/Verluste im Wert von {'{:.2f}'.format(zahlendeSteuern)} angefallen.")
            pdf.ln(6)

metal_steuern = sorted(metal_steuern, key=lambda k: (k["verkaufs Jahr"], k["Asset"]))
crypto_steuern = sorted(crypto_steuern, key=lambda k: (k["verkaufs Jahr"], k["Asset"]))
stock_steuern = sorted(stock_steuern, key=lambda k: (k["verkaufs Jahr"], k["Asset"]))

if metal_steuern[0]["verkaufs Jahr"] != 1990:
    generateTaxPage(metal_steuern, 'metal')
if crypto_steuern[0]["verkaufs Jahr"] != 1990:
    generateTaxPage(crypto_steuern, 'crypto')
if stock_steuern[0]["verkaufs Jahr"] != 1990:
    generateTaxPage(stock_steuern, 'stock')

def summaryTax(metal, crypto, stock, all_years, fiat):

    for year in all_years:
        pdf.add_page()
        th = pdf.font_size+2
        pdf.ln(5)
        pdf.set_font('times', 'B', 12)
        pdf.cell(40, 8,f"Zusammenfassung Steuern {year}:", ln=True)
        pdf.set_font('times', '', 10)
        pdf.ln(th*3)
        amount = 0.00
        temp_amount = 0.00
        if len(metal) > 0:
            pdf.set_font('times', 'B', 10)
            pdf.cell(0, 0,f"Edelmetalle", ln=True)
            pdf.set_font('times', '', 10)
            pdf.ln(th)
            for row in metal:
                if row["verkaufs Jahr"] == year:
                    pdf.ln(th)
                    pdf.cell(0, 0, f'{row["Asset"]}', ln=True)
                    pdf.cell(0, 0, f'{"                                     {:.2f}".format(row["Betrag"])} {fiat}', align='R', ln=True)
                    temp_amount += float(row["Betrag"])
            amount += temp_amount
            pdf.ln(th)
            pdf.cell(0, 0, f'_____________________________________________________________________________________________________________', ln=True)
            pdf.ln(th)
            pdf.cell(0, 0, f'                                       {"{:.2f}".format(temp_amount)} {fiat}', align='R', ln=True)
            pdf.ln(th*2)

        temp_amount = 0.00
        if len(crypto) > 0:
            pdf.set_font('times', 'B', 10)
            pdf.cell(0, 0,f"Cryptowährungen", ln=True)
            pdf.set_font('times', '', 10)
            pdf.ln(th)
            for row in crypto:
                if row["verkaufs Jahr"] == year:
                    pdf.ln(th)
                    pdf.cell(0, 0, f'{row["Asset"]}', ln=True)
                    pdf.cell(0, 0, f'{"                                     {:.2f}".format(row["Betrag"])} {fiat}', align='R', ln=True)
                    temp_amount += float(row["Betrag"])
            amount += temp_amount
            pdf.ln(th)
            pdf.cell(0, 0, f'_____________________________________________________________________________________________________________', ln=True)
            pdf.ln(th)
            pdf.cell(0, 0, f'{"                                       {:.2f}".format(temp_amount)} {fiat}', align='R', ln=True)
            pdf.ln(th*2)

        temp_amount = 0.00
        if len(stock) > 0:
            pdf.set_font('times', 'B', 10)
            pdf.cell(0, 0,f"Aktien", ln=True)
            pdf.set_font('times', '', 10)
            pdf.ln(th)
            for row in stock:
                if row["verkaufs Jahr"] == year:
                    pdf.ln(th)
                    pdf.cell(0, 0, f'{row["Asset"]}', ln=True)
                    pdf.cell(0, 0, f'                                       {"{:.2f}".format(row["Betrag"])} {fiat}', align='R', ln=True)
                    temp_amount += float(row["Betrag"])
            amount += temp_amount
            pdf.ln(th)
            pdf.cell(0, 0, f'_____________________________________________________________________________________________________________', ln=True)
            pdf.ln(th)
            pdf.cell(0, 0, f'                                       {"{:.2f}".format(temp_amount)} {fiat}', align='R', ln=True)
            pdf.ln(th*2)

        pdf.ln(th*4)
        pdf.set_font('times', 'B', 11)
        if year == today.year:
            pdf.cell(45, th, f"In {year} sind bis jetzt Gewinne/Verluste im Wert von {'{:.2f}'.format(amount)} {fiat} zu versteuern.")
            pdf.ln(6)
        else:
            pdf.cell(45, th, f"In {year} sind Gewinne/Verluste im Wert von {'{:.2f}'.format(amount)} {fiat} zu versteuern.")
            pdf.ln(6)

        pdf.ln(6)
        pdf.set_font('times', '', 9)
        pdf.cell(45, th, f"*gezahlte Gebühren werden derzeit noch nicht mit verrechnet, da die *csv Datei keine Asset Preise für Gebühren zur verfügung stellt.", ln=True)

years_tax = []
for row in metal_steuern:
    if row['verkaufs Jahr'] > 1990:
        years_tax.append(row['verkaufs Jahr'])
for row in crypto_steuern:
    if row['verkaufs Jahr'] > 1990:
        years_tax.append(row['verkaufs Jahr'])
for row in stock_steuern:
    if row['verkaufs Jahr'] > 1990:
        years_tax.append(row['verkaufs Jahr'])

years_tax = set(years_tax)

currency = fiat_assets[0]

summaryTax(metal_steuern, crypto_steuern, stock_steuern, years_tax, currency)

################################################################################################
################################## Aktuelles Portfolio #########################################
################################################################################################

def assetsInPortfolio(assets_list, in_dict, asset_class):

    #print(assets_list)
    if len(in_dict) > 0:
        pdf.add_page()
        pdf.ln(5)
        pdf.set_font('times', 'B', 10)
        pdf.cell(40, 8,f"Diese Vermögenswerte, der Anlageklasse {asset_class}, sollten in deinem Portfolio sein:", ln=True)
        pdf.set_font('times', '', 10)
        pdf.ln(12)
        col_width = (pdf_width-30)/6
        pdf.set_font('times', 'B', 10)
        th = pdf.font_size+2

        pdf.cell(col_width, th, "Datum", align='C', border=1)
        pdf.cell(col_width, th, "Transaktion", align='C', border=1)
        pdf.cell(col_width, th, "Betrag", align='C', border=1)
        pdf.cell(col_width, th, "Asset Menge", align='C', border=1)
        pdf.cell(col_width, th, "Asset Preis", align='C', border=1)
        pdf.cell(col_width, th, "HODL Zeit", align='C', border=1)
        pdf.ln(th)
        pdf.set_font('times', '', 9)
        temp_asset = ''
        temp_Amount = 0.00
        temp_Price = 0.00
        hodl_amount = 0.00
        for row in in_dict:
            diftime = today - datetime.strptime(row["Datum"], '%Y-%m-%d %H:%M:%S')
            if row["Asset"] != temp_asset:
                if temp_Price > 0.00:
                    col_width = (pdf_width-30)
                    if hodl_amount > 0.00:
                        pdf.cell(col_width, th, f"Haltefrist 1 Jahr+: {'{:.6f}'.format(hodl_amount)} {temp_asset}", align="R",  border=1)
                        pdf.ln(th)
                    else:
                        pdf.cell(col_width, th, '',  border=1)
                        pdf.ln(th)
                    pdf.cell(col_width, th, f"Investiert: {'{:.2f}'.format(temp_Price)}", align="R", border=1)
                    pdf.ln(th)
                    pdf.cell(col_width, th, f"Gesamt Menge: {'{:.6f}'.format(temp_Amount)} {temp_asset}", align="R",  border=1)
                    pdf.ln(th)
                    pdf.cell(col_width, th, f"Durchschnitt Preis: {'{:.3f}'.format(temp_Price/temp_Amount)}", align="R", border=1)
                    pdf.ln(th)
                temp_asset = row["Asset"]
                pdf.set_font('times', 'B', 10)
                col_width = (pdf_width-30)
                pdf.cell(col_width, th, row["Asset"], align='C', border=1)
                col_width = (pdf_width-30)/6
                pdf.set_font('times', '', 9)
                temp_Amount = 0.00
                temp_Price = 0.00
                pdf.ln(th)

            col_width = (pdf_width-30)/6
            pdf.cell(col_width, th, str(row["Datum"]), border=1)
            pdf.cell(col_width, th, str(row["Transaktion"]), border=1)
            pdf.cell(col_width, th, str(row["Betrag"]), border=1)
            pdf.cell(col_width, th, str(row["Asset Menge"]), border=1)
            pdf.cell(col_width, th, str(row["Asset Preis"]), border=1)
            if float(diftime.days) > 365:
                pdf.set_text_color(0, 255, 0)
                hodl_amount += float(row['Asset Menge'])
            pdf.cell(col_width, th, str(diftime.days), border=1)
            pdf.ln(th)
            pdf.set_text_color(0, 0, 0)
            temp_Amount += float(row["Asset Menge"])
            if row['Betrag'] != '':
                temp_Price += float(row["Betrag"])

        col_width = (pdf_width-30)/4
        if temp_Price > 0.00:
            col_width = (pdf_width-30)
            if hodl_amount > 0.00:
                pdf.cell(col_width, th, f"Haltefrist 1 Jahr+: {'{:.6f}'.format(hodl_amount)} {temp_asset}", align="R",  border=1)
                pdf.ln(th)
            else:
                pdf.cell(col_width, th, '',  border=1)
                pdf.ln(th)
            pdf.cell(col_width, th, f"Investiert: {'{:.2f}'.format(temp_Price)}", align="R", border=1)
            pdf.ln(th)
            pdf.cell(col_width, th, f"Gesamt Menge: {'{:.6f}'.format(temp_Amount)} {temp_asset}", align="R",  border=1)
            pdf.ln(th)
            pdf.cell(col_width, th, f"Durchschnitt Preis: {'{:.3f}'.format(temp_Price/temp_Amount)}", align="R", border=1)
            pdf.ln(th)

if len(metal_portfolio) > 0:
    fin_metal_portfolio = []
    temp_metal_list = []
    for row in metal_list:
        temp_metal_list.append(row['Asset'])
    for row in metal_portfolio:
        if row['Asset'] in temp_metal_list:
            fin_metal_portfolio.append(row)
    assetsInPortfolio(metal_list, fin_metal_portfolio, 'Metal')

if len(crypto_portfolio) > 0:
    fin_crypto_portfolio = []
    temp_crypto_list = []
    for row in crypto_list:
        temp_crypto_list.append(row['Asset'])
    for row in crypto_portfolio:
        if row['Asset'] in temp_crypto_list:
            fin_crypto_portfolio.append(row)
    assetsInPortfolio(crypto_list, fin_crypto_portfolio, 'Crypto')

if len(stock_portfolio) > 0:
    fin_stock_portfolio = []
    temp_stock_list = []
    for row in stock_list:
        temp_stock_list.append(row['Asset'])
    for row in stock_portfolio:
        if row['Asset'] in temp_stock_list:
            fin_stock_portfolio.append(row)
    assetsInPortfolio(stock_list, fin_stock_portfolio, 'Aktien')

################################################################################################
##################################### Last PDF Page ############################################
################################################################################################

btc_adress = 'bc1q09w7rac565vr7dtqvc2j6sv9f8fa4mscv9ct0s'
best_adress = '0x34Fc219cDE52D31BE23D6fA83448B2d1903Df4FD'
iota_adress = 'iota1qqffgg8cmwlh3dqss7s2u6fsl83007505j98zjpqjq9je3ca30cfccrs2uy'

btc_code = pyqrcode.create(btc_adress)
btc_code.png('btc1.png', scale=1, module_color=[0, 0, 0, 128], background=[0xff, 0xff, 0xff])
listOfPNG.append('btc1')
iota_code = pyqrcode.create(iota_adress)
iota_code.png('iota1.png', scale=1, module_color=[0, 0, 0, 128], background=[0xff, 0xff, 0xff])
listOfPNG.append('iota1')
best_code = pyqrcode.create(best_adress)
best_code.png('best1.png', scale=1, module_color=[0, 0, 0, 128], background=[0xff, 0xff, 0xff])
listOfPNG.append('best1')

pdf.add_page()
pdf.ln(5)
pdf.cell(40, 8,f"Gefällt dir diese Anwendung, dann würde ich mich über eine Spende freuen.", ln=True)
pdf.set_font('times', '', 10)
pdf.ln(20)
pdf.cell(40, 8, f"Bitcoin:\n", ln=True)
pdf.image('btc1.png', 100, 55, 20)
pdf.ln(20)
pdf.cell(0, 0, f"{btc_adress}\n", ln=True, align = 'C')
pdf.ln(16)
pdf.cell(40, 8, f"IOTA:\n", ln=True)
pdf.image('iota1.png', 100, 100, 20)
pdf.ln(20)
pdf.cell(0, 0, f"{iota_adress}\n", ln=True, align = 'C')
pdf.ln(16)
pdf.cell(40, 8, f"BEST:\n", ln=True)
pdf.image('best1.png', 100, 145, 20)
pdf.ln(20)
pdf.cell(0, 0, f"{best_adress}\n", ln=True, align = 'C')
pdf.ln(20)
pdf.cell(40, 8,f"Vielen Dank.", ln=True)
pdf.ln(20)
pdf.cell(40, 8,f"Das Script zur Erstellung dieses PDF findet Ihr unter:", ln=True)
pdf.ln(5)
pdf.cell(0, 0,f"{gitlink}", link=gitlink, ln=True, align = 'C')
pdf.ln(20)
pdf.cell(40, 8,f"Hier könnt Ihr auch gerne unter \"Issues\" einen \"New Issue\" anlegen um mir Fehler und Verbesserungsvorschläge zukommen zu lassen.", ln=True)
user = os.environ[userKey]
pdf.output(f'/Users/{user}/Desktop/BP-Report.pdf')

removeFiles(listOfPNG)


"""Authentication-related models for nautobot-netbox-importer."""

from typing import List

from diffsync.enum import DiffSyncModelFlags
import django.contrib.auth.models as auth
import structlog

from .abstract import DjangoBaseModel
from .references import ContentTypeRef, PermissionRef

class Group(DjangoBaseModel):
    """Definition of a user group."""

    _modelname = "group"
    _identifiers = ("name",)
    _attributes = ("permissions",)
    _nautobot_model = auth.Group

    name: str
    permissions: List[PermissionRef] = []

class Permission(DjangoBaseModel):
    """Definition of a permissions rule."""

    _modelname = "permission"
    _identifiers = ("content_type", "codename")
    _attributes = ("name",)
    _nautobot_model = auth.Permission

    content_type: ContentTypeRef
    codename: str

    name: str

    def __init__(self, *args, **kwargs):
        """Set the IGNORE flag on permissions that refer to content-types that do not exist in Nautobot."""
        super().__init__(*args, **kwargs)
        if (
            self.content_type["app_label"]
            not in [
                "auth",
                "circuits",
                "contenttypes",
                "dcim",
                "extras",
                "ipam",
                "references",
                "taggit",
                "tenancy",
                "users",
                "virtualization",
            ]
            or self.content_type["model"] not in self.diffsync.top_level
        ):
            structlog.get_logger().debug(
                "Flagging permission for extraneous content-type as ignorable",
                diffsync=self.diffsync,
                app_label=self.content_type["app_label"],
                model=self.content_type["model"],
            )
            self.model_flags |= DiffSyncModelFlags.IGNORE


import contextlib
from datetime import datetime
import json
from pathlib import Path
import subprocess
import timeit
import uuid

from absl import logging

from deeptangle.utils import ExperimentID

logging.get_absl_handler().setFormatter(None)  # type: ignore

def start_logging(flags, model_dir: str):
    path = Path(model_dir)

    # Define some parameters to characterise the experiment
    info_flags = {
        'uid': str(uuid.uuid4())[:8],
        'hash': _get_git_revision_hash(),
        'date': datetime.today(),
    }

    # Get the parameters from train.py
    training_flags = [v for k, v in flags.flags_by_module_dict().items() if 'train' in k]
    if len(training_flags) > 0:
        training_flags = {v.name: v.value for v in training_flags[0]}
    experiment_flags = {**info_flags, **training_flags}

    if flags.save:
        experiment_dir = path.joinpath(f"{experiment_flags['uid']}")
        experiment_dir.mkdir(parents=True, exist_ok=True)
        filepath = experiment_dir.joinpath('experiment.json')

        flags.alsologtostderr = True
        logging.get_absl_handler().use_absl_log_file('logs', experiment_dir)   # type: ignore

        # Create a symlink to a log file in the parents folder to ease of finding.
        log_symlink = path.joinpath('logs.INFO')
        log_symlink.unlink(missing_ok=True)
        log_symlink.symlink_to(Path(f"{experiment_flags['uid']}").joinpath('logs.INFO'))

        # Save the experiment parameters into a json file.
        with open(filepath, 'w') as f:
            json.dump(experiment_flags, f, indent=4, default=str)
        logging.info('Experiment parameters stored at %s.', filepath)
        logging.info('Experiment logs stored at %s/%s.INFO', experiment_dir, info_flags['uid'])
    else:
        experiment_dir = None
        logging.info('\n'.join(f'- {k}: {v}' for k, v in experiment_flags.items()))
    experiment_id = ExperimentID(info_flags['uid'], experiment_dir)
    return experiment_id

@contextlib.contextmanager
def time_activity(activity_name: str):
    start = timeit.default_timer()
    yield
    duration = timeit.default_timer() - start
    logging.info('[Timing] %s finished: %.4f s.', activity_name, duration)

def _get_git_revision_hash() -> str:
    cmd = ['git', 'rev-parse', 'HEAD']
    return subprocess.check_output(cmd).decode('ascii').strip()

def store_logs(flags, stderr=True):
    flags.alsologtostderr = stderr
    logging.get_absl_handler().use_absl_log_file()  # type: ignore
    logging.info('Logs stored in %s.', flags.log_dir)

def log_step(step, uid, losses, save=False):
    time = datetime.now().strftime('%d/%m/%Y|%H:%M:%S')
    header = f'[{uid}|{time}][{step+1}] '
    loss_text = ' | '.join(f'{k}:{v:8.6g}' for k, v in losses.items())
    text = header + loss_text
    if save:
        text += ' (model saved)'
    logging.info(text)

def recover_experiment_parameters(experiment_dir: str) -> dict:
    experiment_path = Path(experiment_dir)
    filepath = experiment_path.joinpath('experiment.json')
    with open(filepath, 'r') as f:
        exp_data = json.load(f)
    return exp_data


import cv2
import imutils
import numpy as np
import pytesseract

img = cv2.imread("/Users/rasleen/Downloads/opencv-text-recognition/car.jpg")
img = cv2.resize(img, (620,480) )
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #convert to grey scale
gray = cv2.bilateralFilter(gray, 11, 17, 17)
edged = cv2.Canny(gray, 30, 200) #Perform Edge detection
cnts = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:10]
screenCnt = None
# loop over our contours
for c in cnts:
                # approximate the contour
	peri = cv2.arcLength(c, True)
	approx = cv2.approxPolyDP(c, 0.018 * peri, True)
                # if our approximated contour has four points, then
                # we can assume that we have found our screen
	if len(approx) == 4:
		screenCnt = approx
		break
# Masking the part other than the number plate
mask = np.zeros(gray.shape,np.uint8)
new_image = cv2.drawContours(mask,[screenCnt],0,255,-1,)
new_image = cv2.bitwise_and(img,img,mask=mask)
#cv2.imwrite('biketest.jpg',new_image)
# Now crop
(x, y) = np.where(mask == 255)
(topx, topy) = (np.min(x), np.min(y))
(bottomx, bottomy) = (np.max(x), np.max(y))
Cropped = gray[topx:bottomx+1, topy:bottomy+1]
#Read the number plate
cv2.imwrite('10croptest.jpg',Cropped)
text = pytesseract.image_to_string(Cropped, config='--psm 11')

print("Detected Number is:",text)


from django.conf import settings
from django.conf.urls import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.views.generic import TemplateView
from django.views.generic.base import RedirectView

from dg.base_settings import TRAINING_PAGE, TRAINING_APP_PAGE

from tastypie.api import Api
from training.api import TrainerResource, LanguageResource, AssessmentResource, QuestionResource, MediatorResource, DistrictResource, VillageResource, PartnerResource, TrainingResource, StateResource, ScoreResource

from views import login, dashboard, graph_data, get_overall_data, get_filter_data

from training.log.training_log import send_updated_log
from training.admin import training_admin

from social_website.views import picoseekho_view, disseminationprep_view, disseminationform_view, adoptionverification_view

api = Api(api_name = "v1")
api.register(TrainerResource())
api.register(TrainingResource())
api.register(LanguageResource())
api.register(AssessmentResource())
api.register(QuestionResource())
api.register(MediatorResource())
api.register(DistrictResource())
api.register(VillageResource())
api.register(PartnerResource())
api.register(StateResource())
api.register(ScoreResource())

urlpatterns = patterns('',
	url(r'^$', RedirectView.as_view(url=TRAINING_PAGE)),
    url(r'^app/', RedirectView.as_view(url=TRAINING_APP_PAGE)),
    url(r'^login/', login),
    url(r'^api/', include(api.urls)),
    url(r'^analytics/', dashboard),
    url(r'^graph_data', graph_data),
    url(r'^get_log/', send_updated_log),
    url(r'^getCardGraphData', get_overall_data),
    url(r'^get_filter_data',get_filter_data),
    # admin/logout/ should be above admin/ URL
    url(r'^admin/logout/?$', 'django.contrib.auth.views.logout', {'next_page': '/training/admin/'}),
    url(r'^admin/', include(training_admin.urls)),
    url(r'^courseware/$', TemplateView.as_view(template_name='training_hi.html'), name='training'),
    url(r'^courseware/en/$', TemplateView.as_view(template_name='training_en.html'), name='training-el'),
    url(r'^courseware/hi/$', TemplateView.as_view(template_name='training_hi.html'), name='training-hi'),
    url(r'^courseware/fr/$', TemplateView.as_view(template_name='training_fr.html'), name='training-fr'),
    url(r'^courseware/picoseekho/(?P<uid>.+)/$', picoseekho_view, name='picoseekho'),
    url(r'^courseware/picoseekho/$', picoseekho_view, name='picoseekho'),
    url(r'^courseware/dissemination_prep/(?P<uid>.+)/$', disseminationprep_view, name='disseminationprep'),
    url(r'^courseware/dissemination_prep/$', disseminationprep_view, name='disseminationprep'),
    url(r'^courseware/dissemination_form/(?P<uid>.+)/$', disseminationform_view, name='disseminationform'),
    url(r'^courseware/dissemination_form/$', disseminationform_view, name='disseminationform'),
    url(r'^courseware/adoption_verification/(?P<uid>.+)/$', adoptionverification_view, name='adoptionverification'),
    url(r'^courseware/adoption_verification/$', adoptionverification_view, name='adoptionverification'),
    )


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from imdb import IMDb
import json
# oppure jsonpickle???

# JSON snippets from https://github.com/hrishikeshsathe/IMDb-to-JSON
class JSONAttributes:
    title = ''
    duration = ''
    genre = []
    rating= 0.0
    description = ''
    actors = []
    seasons = []
    director = ''
    creators = []
    def __init__(self):
        self.title = ''
        self.duration = ''
        self.genre = []
        self.rating = 0.0
        self.description = ''
        self.actors = []
        self.director = ''
        self.creators = []

    def print_overview(self):
        print("Title: ",self.title)
        print("Duration: ",self.duration)
        print("Genre: ",self.genre)
        print("Rating: ",self.rating)
        print("Description: ",self.description)
        print("Creators: ",self.creators)
        print("Director: ",self.director)
        print("Actors: ",self.actors)

def write_json(json):

    """ write data into file as json object """

    file = open(json.title+'.json','w')
    file.write('{\n')
    file.write('"title":"'+json.title+'",\n')
    file.write('"duration":"'+json.duration+'",\n')
    file.write('"genre":[')
    file.write(','.join('"'+x+'"' for x in json.genre))
    file.write('],\n')
    file.write('"rating":"'+json.rating+'",\n')
    file.write('"description":"'+json.description+'",\n')
    file.write('"director":"'+json.director+'",\n')
    file.write('"creators":[')
    file.write(','.join('"'+x+'"' for x in json.creators))
    file.write('],\n')
    file.write('"actors":[')
    file.write(','.join('"'+x+'"' for x in json.actors))
    file.write('],\n')
    file.write('"seasons":[')
    file.write(','.join('['+episode_list_as_string(season)+']\n' for season in json.seasons))
    file.write(']\n}')
    file.close()
    print('Writing done')

#create json object. check class JSONAttributes(). it just contains variables to store values
json_temp = JSONAttributes()

# create an instance of the IMDb class
ia = IMDb()

# get a movie
movie = ia.get_movie('0060315')

# print the names of the directors of the movie
#print('Actors:')
for director in movie['director']:
    json_temp.director = director
    print(json.dump(json_temp))

# print the genres of the movie
print('Genres:')
for genre in movie['genres']:
    print(genre)


import asyncio
import os
import time
from asyncio.exceptions import TimeoutError, IncompleteReadError
from ssl import SSLError
from urllib.parse import urlparse

from aiohttp import ClientSession, ClientTimeout, ServerDisconnectedError
from aiohttp_socks import ProxyConnector
from python_socks._errors import ProxyConnectionError, ProxyError, ProxyTimeoutError

DATA_FILES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
PROXIES_FILE_PATH = os.path.join(DATA_FILES_PATH, 'proxies.txt')
ALIVE_PROXIES_FILE_PATH = os.path.join(DATA_FILES_PATH, 'alive_proxies.txt')

CHECKING_URL = 'https://google.com/'
CONNECTION_TIMEOUT = 10

async def check_proxy(proxy: str) -> tuple[str, float]:
    connector = ProxyConnector.from_url(proxy)
    session_timeout = ClientTimeout(sock_connect=CONNECTION_TIMEOUT)

    async with ClientSession(connector=connector, timeout=session_timeout) as session:
        start = time.time()
        try:
            await session.get(CHECKING_URL, timeout=CONNECTION_TIMEOUT)
        except (
                ProxyConnectionError, ConnectionResetError, ProxyError, ProxyTimeoutError,
                IncompleteReadError, TimeoutError, ServerDisconnectedError, SSLError,
        ):
            print(f'Proxy {proxy} is DEAD.')
            return proxy, 0
        else:
            response_time = time.time() - start

        prts = urlparse(proxy)
        proxy = f'{prts.scheme}\t{prts.hostname}\t{prts.port}'

        print(f'Proxy {proxy} is ALIVE.')
        return proxy, response_time

async def main() -> None:
    with open(PROXIES_FILE_PATH) as proxies_file:
        proxies = set(proxies_file.read().split('\n'))

    tasks = []
    for proxy in proxies:
        tasks.append(asyncio.create_task(check_proxy(proxy)))

    result = await asyncio.gather(*tasks)

    alive_proxies = [(proxy, response_time) for proxy, response_time in result if response_time > 0]
    sorted_alive_proxies = [proxy for proxy, _ in sorted(alive_proxies, key=lambda x: x[1])]

    with open(PROXIES_FILE_PATH, 'w') as proxies_file, open(ALIVE_PROXIES_FILE_PATH, 'w') as alive_proxies_file:
        proxies_file.write('\n'.join(proxies))
        alive_proxies_file.write('\n'.join(sorted_alive_proxies))

if __name__ == '__main__':
    asyncio.run(main())


#!/usr/bin/env python
import argparse
import logging
import os
import time

import fix_orphaned_shards
import mysql_shard_config
import mysql_failover
import start_shard_migration
from lib import mysql_lib
from lib import host_utils
from lib import environment_specific

log = logging.getLogger(__name__)

REPL_SYNC_MAX_SECONDS = 30
ROW_ESTIMATE_MAX_DIFF = .3
ROW_ACTUAL_MAX_DIFF = .001

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('source_replica_set',
                        help='Which replica set to move shards *FROM*')
    parser.add_argument('--dry_run',
                        help=('Do not actually run a migration, just run '
                              'safety checks, etc...'),
                        default=False,
                        action='store_true')
    parser.add_argument('--confirm_estimated_row_counts',
                        help=('If set, do not confirm row counts, just confirm '
                              'estimate.'),
                        default=True,
                        dest='confirm_row_counts',
                        action='store_false')
    args = parser.parse_args()

    finish_shard_migration(args.source_replica_set,
                           args.dry_run,
                           args.confirm_row_counts)

def finish_shard_migration(source_replica_set,
                           dry_run=False, confirm_row_counts=True):
    """ Run a partial failover to finish a migration

    Args:
    source_replica_set -  The source replica set for the migration
    dry_run - Just run verifications, do not failover shards
    confirm_row_counts - If true, confirm row counts of slaves, otherwise
                         check estimated row counts
    """
    if host_utils.get_security_role() not in environment_specific.ROLE_TO_MIGRATE:
        raise Exception(environment_specific.ROLE_ERROR_MSG)
    # Figure out what we are working on based on the lock present on the
    # source_replica_set
    migration = start_shard_migration.check_migration_lock(source_replica_set)
    if migration['status'] != start_shard_migration.STATUS_FAILOVER_READY:
        raise Exception('Migration is not ready to run')

    destination_replica_set = migration['destination_replica_set']
    source_replica_set = migration['source_replica_set']
    mig_databases = set(migration['mig_databases'].split(', '))
    non_mig_databases = set(migration['non_mig_databases'].split(', '))
    mig_lock_identifier = migration['lock_identifier']

    # Figure out some more data based on the lock
    zk = host_utils.MysqlZookeeper()
    source_master = zk.get_mysql_instance_from_replica_set(source_replica_set)
    destination_master = zk.get_mysql_instance_from_replica_set(destination_replica_set)
    log.info('Will migrate {dbs} from {s} to {d}'
             ''.format(dbs=mig_databases,
                       s=source_replica_set,
                       d=destination_replica_set))
    try:
        dest_lock_identifier = None
        src_lock_identifier = None
        read_only_set = False

        if not dry_run:
            log.info('Taking promotion locks')
            dest_lock_identifier = mysql_failover.get_promotion_lock(destination_replica_set)
            src_lock_identifier = mysql_failover.get_promotion_lock(source_replica_set)

        # Check replication
        log.info('Checking replication')
        check_replication_for_migration(source_replica_set, destination_replica_set)

        # Check schema and data
        log.info('Checking schema and row counts')
        verify_schema_for_migration(source_replica_set, destination_replica_set,
                                    mig_databases, confirm_row_counts)

        log.info('Confirming that no real tables exists on blackhole dbs')
        verify_blackhole_dbs(destination_master, non_mig_databases)

        if dry_run:
            log.info('In dry_run mode, existing now')
            os._exit(environment_specific.DRY_RUN_EXIT_CODE)

        log.info('Preliminary sanity checks complete, starting migration')

        log.info('Setting read_only on master servers')
        read_only_set = True
        mysql_lib.set_global_variable(source_master, 'read_only', True)

        # We don't strictly need to set read_only on the destination_master
        # but it seems sane out of paranoia
        mysql_lib.set_global_variable(destination_master, 'read_only', True)

        log.info('Confirming no writes to old master')
        mysql_failover.confirm_no_writes(source_master)

        log.info('Waiting for replicas to be caught up')
        wait_for_repl_sync(destination_master)

        log.info('Breaking replication from source to destination')
        mysql_lib.reset_slave(destination_master)

        log.info('Renaming migrated dbs on source master')
        fix_orphaned_shards.rename_db_to_drop(source_master, mig_databases,
                                              skip_check=True)

        log.info('Modifying shard mapping')
        migrate_shard_mapping(source_replica_set,
                              destination_replica_set,
                              mig_databases)

        log.info('Dropping blackhole dbs on destination')
        for db in non_mig_databases:
            mysql_lib.drop_db(destination_master, db)

        start_shard_migration.finish_migration_log(mig_lock_identifier,
                                                   start_shard_migration.STATUS_FINISHED)
    except:
        raise

    finally:
        if read_only_set:
            log.info('Remove read_only settings')
            mysql_lib.set_global_variable(source_master, 'read_only', False)
            mysql_lib.set_global_variable(destination_master, 'read_only', False)

        if dest_lock_identifier:
            log.info('Releasing destination promotion lock')
            mysql_failover.release_promotion_lock(dest_lock_identifier)

        if src_lock_identifier:
            log.info('Releasing source promotion lock')
            mysql_failover.release_promotion_lock(src_lock_identifier)

    # No exception got reraised in the the except, things must have worked
    log.info('To drop migrated dbs on source, wait a bit and then run:')
    log.info('/usr/local/bin/mysql_utils/fix_orphaned_shards.py -a '
             'drop -i {}'.format(source_master))

def verify_schema_for_migration(source_replica_set,
                                destination_replica_set,
                                databases,
                                confirm_row_counts):
    """ Confirm that source and destination have schema and row counts in sync

    Args:
    source - A hostaddr instance for the source
    destination -A hostaddr instance for the destination
    dbs - A set of database to check
    confirm_row_counts - If True, check that row counts are very close to
                         synchronized, otherwise do a very cursory check
    """
    zk = host_utils.MysqlZookeeper()
    source_master = zk.get_mysql_instance_from_replica_set(source_replica_set)
    destination_master = zk.get_mysql_instance_from_replica_set(destination_replica_set)
    source_slave = zk.get_mysql_instance_from_replica_set(source_replica_set,
                                                          host_utils.REPLICA_ROLE_SLAVE)
    destination_slave = zk.get_mysql_instance_from_replica_set(destination_replica_set,
                                                          host_utils.REPLICA_ROLE_SLAVE)
    problems = list()
    for db in databases:
        source_tables = mysql_lib.get_tables(source_master, db)
        destination_tables = mysql_lib.get_tables(destination_master, db)

        differences = source_tables.symmetric_difference(destination_tables)
        if differences:
            problems.append('Found table existence mismatch in db {db}: {dif}'
                            ''.format(db=db, dif=differences))

        for table in source_tables:
            if table not in destination_tables:
                pass
            source_def = mysql_lib.show_create_table(source_master, db, table,
                                                     standardize=True)

            destination_def = mysql_lib.show_create_table(destination_master,
                                                          db,
                                                          table,
                                                          standardize=True)

            if source_def != destination_def:
                problems.append('Table definition mismatch db {db} '
                                'table {table}'
                                ''.format(db=db,
                                          table=table))

            cnt_problem = check_row_counts(source_slave, destination_slave,
                                           db, table, exact=confirm_row_counts)
            if cnt_problem:
                problems.append(cnt_problem)

    if problems:
        raise Exception('. '.join(problems))

    log.info('Schema and data appear to be in *NSYNC')

def check_row_counts(source, destination, db, table, exact):
    """ Confirm that source and destination have the same or nearly the
        same row counts

    Args:
    source - A hostaddr instance for the source
    destination -A hostaddr instance for the destination
    db - The database to check
    table - The table to check
    exact - how closely do we want to check this?

    Returns - A message if there is a problem
    """

    if exact:
        source_cnt = mysql_lib.get_row_count(source, db, table)
        destination_cnt = mysql_lib.get_row_count(destination, db, table)
        max_cnt = source_cnt * (1 + ROW_ACTUAL_MAX_DIFF)
        min_cnt = source_cnt * (1 - ROW_ACTUAL_MAX_DIFF)
    else:
        source_cnt = mysql_lib.get_row_estimate(source, db, table)
        destination_cnt = mysql_lib.get_row_estimate(destination, db, table)
        max_cnt = source_cnt * (1 + ROW_ESTIMATE_MAX_DIFF)
        min_cnt = source_cnt * (1 - ROW_ESTIMATE_MAX_DIFF)

    if max_cnt < destination_cnt:
        return ('Table {db}.{table} appears to be too large. '
                'Row count is {cnt} > but max size is {max_cnt} '
                ''.format(db=db,
                          table=table,
                          cnt=destination_cnt,
                          max_cnt=max_cnt))

    if min_cnt > destination_cnt:
        return ('Table {db}.{table} appears to be too small. '
                'Row count is {cnt} < min size {min_cnt} '
                ''.format(db=db,
                          table=table,
                          cnt=destination_cnt,
                          min_cnt=min_cnt))
    return None

def verify_blackhole_dbs(destination, non_mig_databases):
    """ Confirm that non migrated tables have no non-blackhole tables

    Args:
    destination - A hostaddr object
    non_mig_databases - A set of dbs to check
    """
    conn = mysql_lib.connect_mysql(destination)
    cursor = conn.cursor()
    query = ("SELECT COUNT(*) AS 'tbls' "
             "FROM information_schema.tables "
             "WHERE ENGINE !='BLACKHOLE'"
             " AND TABLE_SCHEMA=%(db)s")

    for db in non_mig_databases:
        cursor.execute(query, {'db': db})
        check = cursor.fetchone()
        if check['tbls']:
            raise Exception('Blackhole db {db} has non blackhole table on '
                            'instance {i}'
                            ''.format(db=db,
                                      i=destination))

def check_replication_for_migration(source_replica_set,
                                    destination_replica_set):
    """ Confirm that replication is sane for finishing a shard migration

    Args:
    source_replica_set - Where shards are coming from
    destination_replica_set - Where shards are being sent
    """
    zk = host_utils.MysqlZookeeper()
    source_master = zk.get_mysql_instance_from_replica_set(source_replica_set)
    destination_master = zk.get_mysql_instance_from_replica_set(destination_replica_set)
    source_slave = zk.get_mysql_instance_from_replica_set(source_replica_set,
                                                          host_utils.REPLICA_ROLE_SLAVE)
    destination_slave = zk.get_mysql_instance_from_replica_set(destination_replica_set,
                                                          host_utils.REPLICA_ROLE_SLAVE)

    # First we will confirm that the slave of the source is caught up
    # this is important for row count comparisons
    mysql_lib.assert_replication_unlagged(source_slave,
                                          mysql_lib.REPLICATION_TOLERANCE_NORMAL)

    # Next, the slave of the destination replica set for the same reason
    mysql_lib.assert_replication_unlagged(destination_slave,
                                          mysql_lib.REPLICATION_TOLERANCE_NORMAL)

    # Next, the destination master is relatively caught up to the source master
    mysql_lib.assert_replication_unlagged(destination_master,
                                          mysql_lib.REPLICATION_TOLERANCE_NORMAL)

    # We will also verify that the source master is not replicating. A scary
    # scenario is if the there is some sort of ring replication going and db
    # drops of blackhole db's would propegate to the source db.
    try:
        source_slave_status = mysql_lib.get_slave_status(source_master)
    except mysql_lib.ReplicationError:
        source_slave_status = None

    if source_slave_status:
        raise Exception('Source master is setup for replication '
                        'this is super dangerous!')

    # We will also verify that the destination master is replicating from the
    # source master
    slave_status = mysql_lib.get_slave_status(destination_master)
    master_of_destination_master = host_utils.HostAddr(
            ':'.join((slave_status['Master_Host'],
                      str(slave_status['Master_Port']))))
    if source_master != master_of_destination_master:
            raise Exception('Master of destination {d} is {actual} rather than '
                            'expected {expected} '
                            ''.format(d=destination_master,
                                      actual=master_of_destination_master,
                                      expected=destination_master))
    log.info('Replication looks ok for migration')

def wait_for_repl_sync(instance):
    """ Wait for replication to become synced

    args:
    instance - A hostaddr instance
    """
    start = time.time()
    while True:
        acceptable = True
        try:
            mysql_lib.assert_replication_unlagged(instance,
                                                  mysql_lib.REPLICATION_TOLERANCE_NONE)
        except Exception as e:
            log.warning(e)
            acceptable = False

        if acceptable:
            return
        elif (time.time() - start) > REPL_SYNC_MAX_SECONDS:
            raise Exception('Replication is not in an acceptable state on '
                            'replica {}'.format(instance))
        else:
            log.info('Sleeping for 5 second to allow replication to catch up')
            time.sleep(5)

def migrate_shard_mapping(source_replica_set, destination_replica_set,
                          databases):
    """ Update the shard mapping for a migration

    source_replica_set - The source replica set
    destination_replica_set - The desintation replica set
    databases - A set of databases on the source replica set to
                be migrated
    """
    zk = host_utils.MysqlZookeeper()
    shard_config = mysql_shard_config.MySqlShardConfig(use_test_config=False)
    for db in databases:
        (service, namespace, shard) = zk.find_shard(source_replica_set, db)
        shard_config.migrate_shard(service, namespace, shard,
                                   source_replica_set,
                                   destination_replica_set)
    shard_config.push_config()

if __name__ == "__main__":
    environment_specific.initialize_logger()
    main()


# -*- coding: utf-8 -*-
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Import
import numpy as np
import matplotlib.pyplot as plt

from .utils.activation import Activation
from .utils.cost_functions import CostFunctions

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------

class Perceptron:
    """
    A simple implementation of a Perceptron.

    Parameters
    ----------
    n_iter : int, optional
        The number of iterations to train the Perceptron for, by default 100.
    learning_rate : float, optional
        The learning rate for the Perceptron, by default 0.1.
    activation : str, optional
        The activation function to use for the Perceptron, by default 'sigmoid'.
        Can take 'sigmoid', 'tanh' or 'relu' values

    Attributes
    ----------
    W : np.ndarray
        The weights for the Perceptron.
    b : np.ndarray
        The bias for the Perceptron.

    Methods
    -------
    fit(X: np.ndarray, y: np.ndarray) -> None:
        Trains the Perceptron on the given data.
    predict(X: np.ndarray) -> np.ndarray:
        Predicts the output for the given input using the trained Perceptron.
    """
    def __init__(self, n_iter: int = 100, learning_rate: float = 0.1, activation: str = 'sigmoid'):
        """
        Initializes a new instance of the Perceptron class.

        Parameters
        ----------
        n_iter : int, optional
            The number of iterations to train the Perceptron for, by default 100.
        learning_rate : float, optional
            The learning rate for the Perceptron, by default 0.1.
        activation : str, optional
            The activation function to use for the Perceptron, by default 'sigmoid'.
        """
        self.n_iter = n_iter
        self.learning_rate = learning_rate
        try :
            self.activation = getattr(Activation, activation)
        except :
            raise ValueError(f'Unknown activation function: {activation}')

    def __initialize_weights(self, input_size: int) -> None:
        """
        Initializes the weights for the Perceptron.

        Parameters
        ----------
        input_size : int
            The number of features in the input data.
        """
        self.W = np.random.randn(input_size, 1)
        self.b = np.random.randn(1)

    def __compute_gradients(self, X: np.ndarray, y: np.ndarray) -> tuple:
        """
        Computes the gradients for weights and bias.

        Parameters
        ----------
        X : np.ndarray
            The input data.
        y : np.ndarray
            The target values.

        Returns
        -------
        tuple
            Tuple containing gradients for weights and bias.
        """
        Z = X.dot(self.W) + self.b
        A = self.activation(Z)
        dW = 1 / len(y) * np.dot(X.T, A - y)
        db = 1 / len(y) * np.sum(A - y)
        return dW, db

    def __update_weights(self, dW: np.ndarray, db: np.ndarray) -> None:
        """
        Updates the weights and bias using the computed gradients.

        Parameters
        ----------
        dW : np.ndarray
            Gradients for weights.
        db : np.ndarray
            Gradient for bias.
        """
        self.W -= self.learning_rate * dW
        self.b -= self.learning_rate * db

    def fit(self, X: np.ndarray, y: np.ndarray, record_loss: bool = False) -> None:
        """
        Trains the Perceptron on the given data.

        Parameters
        ----------
        X : np.ndarray
            The input data to train the Perceptron on.
        y : np.ndarray
            The target values for the input data.
        """
        self.__initialize_weights(X.shape[1])
        if record_loss:
            self.losses = []

        for _ in range(self.n_iter):
            dW, db = self.__compute_gradients(X, y)
            self.__update_weights(dW, db)

            if record_loss:

                A = self.activation(X.dot(self.W) + self.b)
                loss = CostFunctions.log_loss(y, A)
                self.losses.append(loss)

    def predict(self, X: np.ndarray) -> np.ndarray:
        """
        Predicts the output for the given input using the trained Perceptron.

        Parameters
        ----------
        X : np.ndarray
            The input data to predict the output for.

        Returns
        -------
        np.ndarray
            The predicted output for the input data.
        """
        Z = X.dot(self.W) + self.b
        if self.activation == Activation.sigmoid:
            A = self.activation(Z)
            return A >= 0.5
        elif self.activation == Activation.tanh:
            A = self.activation(Z)
            return A >= 0
        elif self.activation == Activation.relu:
            A = self.activation(Z)
            return A >= 0
        else:
            raise ValueError(f'Unknown activation function: {self.activation}')

    def plot_decision_boundary(self, X, y, title="Decision Boundaries"):
        plt.scatter(X[:, 0], X[:, 1], c=y, cmap="viridis", label="Data Points")
        x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1), np.arange(y_min, y_max, 0.1))
        Z = self.predict(np.c_[xx.ravel(), yy.ravel()])
        Z = Z.reshape(xx.shape)
        plt.contour(xx, yy, Z, alpha=0.3, levels=[0], linestyles='dashed', colors='blue', label="Decision Boundary")
        plt.xlabel('Feature 1')
        plt.ylabel('Feature 2')
        plt.title(title)
        plt.legend()
        plt.show()

    def get_loss_history(self) -> list:
        """
        Returns the recorded loss values during training.

        Returns
        -------
        list
            List of loss values recorded during training.
        """
        if hasattr(self, 'losses'):
            return self.losses
        else:
            print("Loss history not available. Train the model with record_loss=True to capture loss values.")
            return []

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------


import os
import cv2
import glob
import numpy as np
import random
import string

def get_random_string(length):
    letters = string.ascii_lowercase + string.ascii_uppercase
    result_str = ''.join(random.choice(letters) for i in range(length))
    return result_str

def colorjitter(img, cj_type="b"):

    if cj_type == "b":
        value = np.random.choice(np.array([-50, -40, -30, 30, 40, 50]))
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        if value >= 0:
            lim = 255 - value
            v[v > lim] = 255
            v[v <= lim] += value
        else:
            lim = np.absolute(value)
            v[v < lim] = 0
            v[v >= lim] -= np.absolute(value)

        final_hsv = cv2.merge((h, s, v))
        img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
        return img

    elif cj_type == "s":
        value = np.random.choice(np.array([-50, -40, -30, 30, 40, 50]))
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        if value >= 0:
            lim = 255 - value
            s[s > lim] = 255
            s[s <= lim] += value
        else:
            lim = np.absolute(value)
            s[s < lim] = 0
            s[s >= lim] -= np.absolute(value)

        final_hsv = cv2.merge((h, s, v))
        img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
        return img

    elif cj_type == "c":
        brightness = 10
        contrast = random.randint(40, 100)
        dummy = np.int16(img)
        dummy = dummy * (contrast / 127 + 1) - contrast + brightness
        dummy = np.clip(dummy, 0, 255)
        img = np.uint8(dummy)
        return img

def noisy(img, noise_type="gauss"):

    if noise_type == "gauss":
        image = img.copy()
        mean = 0
        st = 0.7
        gauss = np.random.normal(mean, st, image.shape)
        gauss = gauss.astype('uint8')
        image = cv2.add(image, gauss)
        return image

    elif noise_type == "sp":
        image = img.copy()
        prob = 0.05
        if len(image.shape) == 2:
            black = 0
            white = 255
        else:
            colorspace = image.shape[2]
            if colorspace == 3:  # RGB
                black = np.array([0, 0, 0], dtype='uint8')
                white = np.array([255, 255, 255], dtype='uint8')
            else:  # RGBA
                black = np.array([0, 0, 0, 255], dtype='uint8')
                white = np.array([255, 255, 255, 255], dtype='uint8')
        probs = np.random.random(image.shape[:2])
        image[probs < (prob / 2)] = black
        image[probs > 1 - (prob / 2)] = white
        return image

def filters(img, f_type="blur"):

    if f_type == "blur":
        image = img.copy()
        fsize = 9
        return cv2.blur(image, (fsize, fsize))

    elif f_type == "gaussian":
        image = img.copy()
        fsize = 9
        return cv2.GaussianBlur(image, (fsize, fsize), 0)

    elif f_type == "median":
        image = img.copy()
        fsize = 9
        return cv2.medianBlur(image, fsize)

inputFolder = "data\\first"
inputFolder2 = "data\\second"
output = 'augm/first/'
output2 = 'augm/second/'
folderLen = len(inputFolder)
i = 0

for img in glob.glob(inputFolder + "/*.jpg"):

    image = cv2.imread(img)
    r1 = colorjitter(image, cj_type='b')
    r2 = colorjitter(image, cj_type='s')
    r3 = colorjitter(image, cj_type='c')
    cv2.imwrite(output + get_random_string(8) + '.jpg', r1)
    cv2.imwrite(output + get_random_string(8) + '.jpg', r2)
    cv2.imwrite(output + get_random_string(8) + '.jpg', r3)

    r1 = noisy(image, noise_type="gauss")
    r2 = noisy(image, noise_type="sp")
    cv2.imwrite(output + get_random_string(8) + '.jpg', r1)
    cv2.imwrite(output + get_random_string(8) + '.jpg', r2)

    r1 = filters(image, f_type="blur")
    r2 = filters(image, f_type="gaussian")
    r3 = filters(image, f_type="median")
    cv2.imwrite(output + get_random_string(8) + '.jpg', r1)
    cv2.imwrite(output + get_random_string(8) + '.jpg', r2)
    cv2.imwrite(output + get_random_string(8) + '.jpg', r3)

for img in glob.glob(inputFolder2 + "/*.jpg"):

    image = cv2.imread(img)
    r1 = colorjitter(image, cj_type='b')
    r2 = colorjitter(image, cj_type='s')
    r3 = colorjitter(image, cj_type='c')
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r1)
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r2)
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r3)

    r1 = noisy(image, noise_type="gauss")
    r2 = noisy(image, noise_type="sp")
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r1)
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r2)

    r1 = filters(image, f_type="blur")
    r2 = filters(image, f_type="gaussian")
    r3 = filters(image, f_type="median")
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r1)
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r2)
    cv2.imwrite(output2 + get_random_string(8) + '.jpg', r3)

# most of this script was made by tranleanh and his github can be found at https://github.com/tranleanh