import requests
from bs4 import BeautifulSoup
import json

def get_pinned(github_user):
    URL = f"https://github.com/{github_user}"
    page = requests.get(URL)
    soup = BeautifulSoup(page.content, "html.parser")
    pinned_data = soup.find_all("div", {"class": "pinned-item-list-item-content"})
    pinned_posts = []

    for post in pinned_data:
        pinned_posts.append(post.find("a")["href"])

    return pinned_posts

def get_projects(github_user, query):
    URL = f"https://github.com/{github_user}?tab=repositories&q={query}&type=source"
    page = requests.get(URL)

    soup = BeautifulSoup(page.content, "html.parser")
    projects = soup.body.find("ul", {"data-filterable-for": "your-repos-filter"})
    if not projects:
        return []

    projects = projects.find_all("li")
    projects_parsed = []

    for project in projects:
        project_data = {}
        title = project.find("h3").a
        project_data["name"] = title.text.strip().replace("-", " ").capitalize()
        project_data["link"] = title["href"]
        project_data["tags"] = [query]

        impact = project.find("div", class_="f6 color-text-secondary mt-2")

        if impact:
            impact = impact.find_all("a")
            for data in impact:
                project_data[data["href"].split("/")[-1]] = int(data.text.strip())

            if "stargazers" not in project_data:
                project_data["stargazers"] = 0

            if "members" not in project_data:
                project_data["members"] = 0

            project_data["score"] = project_data["stargazers"] + project_data["members"] * 5
        else:
            project_data["score"] = 0

        projects_parsed.append(project_data)

    return projects_parsed

def get_youtube_data(youtube_username):
    initial_data = "var ytInitialData = "
    final_data = ";"

    url = f"https://www.youtube.com/{youtube_username}/videos"
    page = requests.get(url)
    soup = BeautifulSoup(page.content, "html.parser")
    scripts = soup.body.find_all("script")

    videos_data = []

    for script in scripts:
        data = script.encode_contents().decode(errors="replace")
        if initial_data not in data:
            continue
        data = data.replace(initial_data, "").replace(final_data, "")
        tab_renderers = json.loads(data)["contents"]
        tab_renderers = tab_renderers["twoColumnBrowseResultsRenderer"]["tabs"]

        for tab in tab_renderers:
            if "tabRenderer" not in tab:
                continue

            if tab["tabRenderer"]["title"] != "Videos":
                continue

            videos = tab["tabRenderer"]["content"]["sectionListRenderer"]
            videos = videos["contents"][0]["itemSectionRenderer"]
            videos = videos["contents"][0]["gridRenderer"]["items"]

            for video in videos:
                if "gridVideoRenderer" not in video:
                    continue

                video = video["gridVideoRenderer"]

                published = ""
                if "publishedTimeText" in video:
                    published = video["publishedTimeText"]["simpleText"]

                view_count_text = ""
                if "simpleText" in video["viewCountText"]:
                    view_count_text = video["viewCountText"]["simpleText"]

                video_data = {
                    "thumbnail": video["thumbnail"]["thumbnails"][-1]["url"],
                    "title": video["title"]["runs"][0]["text"],
                    "published": published,
                    "viewCountText": view_count_text,
                    "url": f"https://www.youtube.com/watch?v={video['videoId']}",
                }
                videos_data.append(video_data)
    return videos_data


""" File: eulerCharacteristics.py
Description: calculates the characteristics of the 2D Euler equation.
This includes the flux and the eigenvectors associated with it
Author: Pierre-Yves Taunay
Date: November 2018

"""

import numpy as np
from utils import P_from_Ev

GAM = 1.4

def compute_euler_flux(U,direction):
    rho = U[:,0]
    u = U[:,1] / rho
    v = U[:,2] / rho
    E = U[:,3] / rho

    P = P_from_Ev(E,rho,u,v)

    flx = np.zeros(U.shape)

    if direction == 'dx':
        flx[:,0] = rho*u
        flx[:,1] = rho*u**2 + P
        flx[:,2] = rho*u*v
        flx[:,3] = rho*E*u + P*u

    elif direction == 'dy':
        flx[:,0] = rho*v
        flx[:,1] = rho*u*v
        flx[:,2] = rho*v**2 + P
        flx[:,3] = rho*E*v + P*v

    return flx

def eigenvector_x(u,v,a,q,h,nunk):
    Rj = np.zeros((nunk,nunk))
    Lj = np.zeros((nunk,nunk))

    # Right eigenvector
    Rj[0,:] = np.ones((1,nunk))
    Rj[0,-1] = 0

    Rj[1,0] = u - a
    Rj[1,1] = u
    Rj[1,2] = u + a
    Rj[1,3] = 0

    Rj[2,0:3] = v
    Rj[2,3] = -1

    Rj[3,0] = h - a * u
    Rj[3,1] = q
    Rj[3,2] = h + a * u
    Rj[3,3] = -v

    # Left eigenvector
    Lj[0,0] = (GAM-1)*q + a*u
    Lj[0,1] = (1-GAM)*u - a
    Lj[0,2] = (1-GAM)*v
    Lj[0,3] = (GAM-1)
    Lj[0,:] /= (2*a**2)

    Lj[1,0] = a**2 - (GAM-1)*q
    Lj[1,1] = (GAM-1)*u
    Lj[1,2] = (GAM-1)*v
    Lj[1,3] = (1-GAM)
    Lj[1,:] /= a**2

    Lj[2,0] = (GAM-1)*q - a*u
    Lj[2,1] = (1-GAM)*u + a
    Lj[2,2] = (1-GAM)*v
    Lj[2,3] = (GAM-1)
    Lj[2,:] /= (2*a**2)

    Lj[3,0] = v
    Lj[3,2] = -1

    return Rj, Lj

def eigenvector_y(u,v,a,q,h,nunk):
    Rj = np.zeros((nunk,nunk))
    Lj = np.zeros((nunk,nunk))

    # Right eigenvector
    Rj[0,:] = np.ones((1,nunk))
    Rj[0,-1] = 0

    Rj[1,0:3] = u
    Rj[1,3] = 1

    Rj[2,0] = v - a
    Rj[2,1] = v
    Rj[2,2] = v + a
    Rj[2,3] = 0

    Rj[3,0] = h - a * v
    Rj[3,1] = q
    Rj[3,2] = h + a * v
    Rj[3,3] = u

    # Left eigenvector
    Lj[0,0] = (GAM-1)*q + a*v
    Lj[0,1] = (1-GAM)*u
    Lj[0,2] = (1-GAM)*v - a
    Lj[0,3] = (GAM-1)
    Lj[0,:] /= (2*a**2)

    Lj[1,0] = a**2 - (GAM-1)*q
    Lj[1,1] = (GAM-1)*u
    Lj[1,2] = (GAM-1)*v
    Lj[1,3] = (1-GAM)
    Lj[1,:] /= a**2

    Lj[2,0] = (GAM-1)*q - a*v
    Lj[2,1] = (1-GAM)*u
    Lj[2,2] = (1-GAM)*v + a
    Lj[2,3] = (GAM-1)
    Lj[2,:] /= (2*a**2)

    Lj[3,0] = -u
    Lj[3,1] = 1

    return Rj, Lj

def compute_eigenvector(U,U0,direction):
    rho = U[:,0]
    u = U[:,1] / rho
    v = U[:,2] / rho
    E = U[:,3] / rho

    P = P_from_Ev(E,rho,u,v)

    nunk = U.shape[1]
    nelem = U.shape[0]

    a = np.sqrt(GAM*P/rho)
    q = 1/2*(u**2 + v**2) # Dynamic pressure
    h = a**2/(GAM-1) + q # Enthalpy

    rho0 = U0[:,0]
    u0 = U0[:,1] / rho0
    v0 = U0[:,2] / rho0
    E0 = U0[:,3] / rho0

    P0 = P_from_Ev(E0,rho0,u0,v0)

    nunk = U0.shape[1]
    nelem = U0.shape[0]

    a0 = np.sqrt(GAM*P0/rho0)
    q0 = 1/2*(u0**2 + v0**2) # Dynamic pressure
    h0 = a0**2/(GAM-1) + q0 # Enthalpy

    Rjlist = []
    Ljlist = []

    if direction == 'dx':
        Rlhs0, Llhs0 = eigenvector_x(u0[0],v0[0],a0[0],q0[0],h0[0],nunk)
        for idx in range(nelem):
            Rj, Lj = eigenvector_x(u[idx],v[idx],a[idx],q[idx],h[idx],nunk)
            Rjlist.append(Rj)
            Ljlist.append(Lj)
        Rlhs0pre = None
        Llhs0pre = None

    elif direction == 'dy':
        # For the y-direction, the bottom boundary can either be pre or post-shock
        Rlhs0, Llhs0 = eigenvector_y(u0[0],v0[0],a0[0],q0[0],h0[0],nunk)
        Rlhs0pre, Llhs0pre = eigenvector_y(u0[-1],v0[-1],a0[-1],q0[-1],h0[-1],nunk)
        for idx in range(nelem):
            Rj, Lj = eigenvector_y(u[idx],v[idx],a[idx],q[idx],h[idx],nunk)
            Rjlist.append(Rj)
            Ljlist.append(Lj)

    Rj = Rjlist
    Lj = Ljlist

    return Rj,Lj,Rlhs0,Llhs0,Rlhs0pre,Llhs0pre

def to_characteristics(U,flx,U0,flx0,order,Lh,alpha,Nx,Ny,direction,options,tc,lambda_calc_char):
    nelem = U.shape[0]
    nunk = U.shape[1]

    # Matrix holders
    V = np.zeros((nelem,order+1,nunk))
    VLF = np.zeros((nelem,order+1,nunk))
    H = np.zeros((nelem,order+1,nunk))

    # For all elements, evaluate R_{i+1/2}^-1 * [STENCIL]
    # The conditional work for r = 2 (order 5)
    # We do the characteristics calculation for all elements for the whole stencil
    V,H,VLF = lambda_calc_char(U,flx,U0,flx0,order,Lh,alpha,direction,tc)

    return V,H,VLF


import numpy
from PIL import Image

def histogram(img):
    image_gray = img.convert('L')
    killy = numpy.array(image_gray)
    maximum = numpy.max(killy)
    minimum = numpy.min(killy)
    dim = maximum - minimum + 1
    hist, bins = numpy.histogram(killy, bins=dim)
    return hist

image1 = Image.open("image1.jpg")
hist1 = histogram(image1)

image2 = Image.open("image2.jpg")
hist2 = histogram(image2)

image3 = Image.open("image3.jpg")
hist3 = histogram(image3)

image4 = Image.open("image4.jpg")
hist4 = histogram(image4)

image5 = Image.open("image5.jpg")
hist5 = histogram(image5)

image6 = Image.open("image6.jpg")
hist6 = histogram(image6)

image7 = Image.open("image7.jpg")
hist7 = histogram(image7)

image8 = Image.open("image8.jpg")
hist8 = histogram(image8)

image9 = Image.open("image9.jpg")
hist9 = histogram(image9)

image10 = Image.open("image10.jpg")
hist10 = histogram(image10)

def calcul_distance(h1, h2):
    size1 = len(h1)
    size2 = len(h2)
    somme = 0
    somme2 = 0
    if size2 > size1:
        for i in range(size2):
            if i < size1:
                somme = somme + (min(h1[i], h2[i]))
            else:
                somme = somme + h2[i]
    else:
        for i in range(size1):
            if i < size2:
                somme = somme + (min(h1[i], h2[i]))
            else:
                somme = somme + h1[i]

    for i in range(size1):
        somme2 = somme2 + h1[i]
    distance = 1 - somme / somme2
    return distance

distance1 = calcul_distance(hist1, hist2)
distance2 = calcul_distance(hist1, hist3)
distance3 = calcul_distance(hist1, hist4)
distance4 = calcul_distance(hist1, hist5)
distance5 = calcul_distance(hist1, hist6)
distance6 = calcul_distance(hist1, hist7)
distance7 = calcul_distance(hist1, hist8)
distance8 = calcul_distance(hist1, hist9)
distance9 = calcul_distance(hist1, hist10)

dictionary = {"image2": distance1, "image3": distance2, "image4": distance3, "image5": distance4,
              "image6": distance5, "image7": distance6, "image8": distance7, "image9": distance8,
              "image10": distance9}

print("Order of similarity used the request image <<image1>> : ")
for w in sorted(dictionary, key=lambda x: dictionary[x] if x in dictionary else None, reverse=False):
    print(w, dictionary[w])


import random
import os

def get_answer() -> bool:
    """
    functions gets an answer which decides if
    some actions will be done
    :return:
    """
    x = input(str("press y for yes or n for no:"))
    while x != "y" and x != "n":
        print("you have entered a wrong answer")
        x = input(str("press y for yes or n for no:"))
    return x == "y"

def get_color_biome(biome):
    """

    :param biome:
    :return:
    """
    color = ""
    obstacles = ""
    if biome == "grass":
        color = "\033[2;32;42m"
        obstacles = ["🌳", "🌲", "🌾"]
    elif biome == "snow":
        color = "\033[2;32;47m"
        obstacles = ["🎄", "🎁", "⛄"]
    elif biome == "dessert":
        color = "\033[2;32;43m"
        obstacles = ["🏜", "🌴", "🏵"]
    elif biome == "hell":
        color = "\033[2;32;41m"
        obstacles = ["🌋", "🔥", "📍"]
    elif biome == "unicorn":
        color = "\033[2;32;45m"
        obstacles = ["🌈", "🍧", "🌺", "🦄"]

    return color, obstacles

def get_correct_int_input(message) -> int:
    """

    :return:
    """
    while True:
        try:
            age = int(input(message))
        except ValueError:
            print(f'error input must be written with digits')
        else:
            break
    return age

def generate_room_length_height():
    """

    :return:
    """
    length = random.choice(range(8, 12))
    height = random.choice(range(6, 9))
    return length, height

def generate_rooms(biome: str, length: int, height: int) -> list:
    """
    "🧙" +🌳🌲🌾⛄🎁🎄
    :return:
    """
    output = []
    color, obstacles = get_color_biome(biome)
    output.append(("  " * (length + 2)))
    for i in range(height):
        obstacle_column = [random.choice(range(length)) for _ in range(random.choice(range(1, 3)))]
        obstacle_column.sort()
        obstacle_column.append(" ")
        # print(obstacle_column)
        # print(obstacle_column[0])
        obstacle_count = 0
        row = ""
        for i_1 in range(length):
            if i_1 == obstacle_column[obstacle_count]:
                row += f"{random.choice(obstacles)}"
                obstacle_count += 1
            else:
                row += f"  "
        output.append(row)
    output.append(("  " * (length + 2)))

    return output

def print_room(room: str, biome: str):
    """

    :param room:
    :param biome:
    :return:
    """
    print(f"\033[2;32;40m{room[0]}\033[0;0m")
    color, obstacles = get_color_biome(biome)
    margins = '\033[2;32;40m  '
    for item in room[1:-1]:
        print(f"{margins}{color}{item}{margins}\033[0;0m")
    print(f"\033[2;32;40m{room[-1]}\033[0;0m")

def get_random_biome() -> str:
    """

    :return:
    """
    return random.choice(["grass", "snow", "dessert", "hell", "unicorn"])

def add_object_on_map(room: list, hero: str, column: int, row: int):
    """
    :param hero:
    :param room:
    :param column:
    :param row:
    :return:
    """
    column = column - 1
    output = []
    count_row_positions = 0
    count_row_positions_actions = 0
    last_ch = 0
    space_len = 1

    for i, item in enumerate(room):
        if i == row:
            if column == 0:
                if item[0] == " ":
                    output.append(f"{hero}{item[2::]}")
                else:
                    output.append(f"{hero}{item[1::]}")
            else:
                for i, ch in enumerate(item):

                    if ch == " ":
                        count_row_positions += 1
                        count_row_positions_actions += 1
                    elif ch != " ":
                        count_row_positions += 1
                        count_row_positions_actions += 2
                    last_ch = i

                    if count_row_positions_actions >= column * 2:
                        break
                try:
                    if item[i + 1] == " ":
                        space_len = 2
                except IndexError:
                        space_len = 1
                output.append(f"{item[0:count_row_positions]}{hero}{item[count_row_positions + space_len::]}")
        else:
            output.append(item)
    return output

def choose_door(biome: str):
    """

    :param biome:
    :return:
    """
    dor = ""
    if biome == "grass":
        dor = "⛩"
    elif biome == "snow":
        dor = "🏛"
    elif biome == "dessert":
        dor = "🛕"
    elif biome == "hell":
        dor = "⛩"
    elif biome == "unicorn":
        dor = "🏛"
    return dor

def choose_enemy():
    """

    :return:
    """
    return random.choice(['💀', '👺', '👹', '🕷', '🦂', '👻', '👽', '😈', '🌬', '🎃', '👾', '🐲', "🎭"])

def enemy_name(how_many, symbol):
    pass

def get_hero_name() -> str:
    """
    If the username doesn't have unknown characters it returns the name
    :return:
    """
    main_path = os.getcwd()
    os.chdir("Your_characters")
    while True:
        username = input(str("Enter your username:"))

        if len(username) >= 4 and f"{username}.txt" not in os.listdir():
            x = 0
            for ch in username:
                if ch in '!"#$%&\'*()+,/:;<=>?@[\\]^`{|}~':
                    x = 1
            if x == 0:
                break
            else:
                print("Error: username cannot contain any of these characters !\"#$%&\'*+,/:;<=>?@[\\]^`{|}~")
        else:
            if len(username) < 4:
                print("Error: username must have at least 4 characters")
            else:
                print("username already used")
    os.chdir(main_path)
    return username

def write_hero_data(hero_name: str, hero_class: str, level: str,
                    gold: str, materials: dict, gear: dict, inventory: list, stats):
    """

    :return:
    """

    main_path = os.getcwd()
    new_data = [hero_class, level, gold]
    for k, v in materials.items():
        new_data.append(f"{v}")
    for k_1, v_1 in gear.items():
        new_data.append(f"{v_1}")
    for item in inventory:
        new_data.append(item)
    for item_1 in stats:
        new_data.append(item_1)
    os.chdir("Your_characters")

    with open(f"{hero_name}.txt", "w") as fw:
        for item in new_data:
            fw.write(item + "\n")

    os.chdir(main_path)

def read_abilities(selected_hero: str):
    """

    :param selected_hero:
    :return:
    """
    main_path = os.getcwd()
    os.chdir("heroes")
    for item in os.listdir():
        if item[0:4] == selected_hero[0:4]:
            with open(item, "r") as fr:
                content = fr.readlines()
    os.chdir(main_path)
    output = [item.strip() for item in content]
    for item in output:
        print(item)

def create_hero():
    """

    :return:
    """
    name = ""
    main_while_break = 0
    print("")
    print(
        "Choose a class for your hero:\nWizard(🧙)\nShadow(👤)\nVampire(🧛)\nfairy.txt(🧚)\nTriton(🧜)\nSpirit(🧞)")
    hero_class = str.lower(input("Choose one option by writing the class name:"))
    classes = ["wizard", "shadow", "fairy"]  # ["wizard", "shadow", "vampire", "fairy", "triton", "spirit"]
    classes_stats = {"wizard": [40, 850, 45, 200, 0, 0, 3, 5, 0, 0, 70], "fairy": [32, 1050, 40, 175, 15, 50, 7, 7, 0, 15, 60]}
    while True:

        while hero_class not in classes:
            print("incorrect class, try again")
            hero_class = str.lower(input("Choose one option by writing the class name:"))
        with open("hero_class_description", "r") as fr:
            content = fr.readlines()
        for item in content:
            if str.lower(item[0:4]) == str.lower(hero_class[0:4]):
                print(f"\n{item}\n")
        print("Choose one option:\ngo back(b)\nchoose class(c)\nread abilities(a)")
        option = str.lower(input("Your option:"))
        while option:
            if option not in ["c", "b", "a"]:
                print("option is incorrect")
            elif option == "a":
                read_abilities(hero_class)
            elif option == "c":
                items_0 = {"helm": "empty_", "chest": "empty_", "boots": "empty_", "weapon": "empty_",
                           "sec_weapon": "empty_", "jewel": "empty_"}
                materials_0 = {"wood": '0', "iron": '0', "diamond": '0', "angelic dust": '0'}
                name = get_hero_name()
                write_hero_data(name, hero_class, "0", "500", materials_0, items_0, ["empty_"] * 18)
                main_while_break = 1
                break
            else:
                break
            print("\nChoose one option:\ngo back(b)\nchoose class(c)\nread abilities(a)")
            option = str.lower(input("Your option:"))

        if main_while_break == 1:
            break
        else:
            print("")
            print("Choose a class for your hero:\nWizard(🧙)\nShadow(👤)\nVampire(🧛)"
                  "\nfairy.txt(🧚)\nTriton(🧜)\nSpirit(🧞)")
            hero_class = str.lower(input("Choose one option by writing the class name:"))
            classes = ["wizard", "shadow", "vampire", "fairy", "triton", "spirit"]
    return f"{name}.txt"

def get_hero_symbol(hero_name):
    """

    :param hero_name:
    :return:
    """
    output = ""
    changed_directory = 0
    main_path = os.getcwd()

    if main_path != "C:\\Users\\andre\\PycharmProjects\\The-Big-Book-of-Small-Python-Projects-solved-by-ramuica-\\Angeilic Powers\\Your_characters":
        os.chdir("Your_characters")
        changed_directory = 1
    for item in os.listdir():
        if item == f"{hero_name}.txt":
            with open(item, "r") as fr:
                content = fr.readlines()
    hero_and_class = {"wizard": "🧙", "shadow": "👤", "vampire": "🧛", "fairy": "🧚", "triton": "🧜", "spirit": "🧞"}
    for k, v in hero_and_class.items():
        if k == content[0].strip():
            output = v
    if changed_directory == 1:
        os.chdir(main_path)
    return output

def get_item_symbol(item):
    """

    :param item:
    :return:
    """

    output = ""
    dict_items = {"crown": "👑", "wand": "🎆",
                  "the_eye": "🧿", "robe": "👘",
                  "talisman": "🎐", "boots": "👢"}

    for k, v in dict_items.items():
        if item == k:
            output = v
            break
    return output

def play_existing_hero():
    """

    :return:
    """
    main_path = os.getcwd()
    os.chdir("Your_characters")
    while True:
        print("\nChoose what hero do you want to play:")
        for i, item in enumerate(os.listdir()):
            symbol = get_hero_symbol(item[0:-4])
            print(f"{item[0:-4]}{symbol}({i + 1})")
        option = get_correct_int_input("\nChoose a hero by writing his corresponding number: ")
        if 0 < option <= len(os.listdir()):
            output = os.listdir()[option - 1]
            break
        else:
            print("the option you choose doesn't correspond to any hero")
    os.chdir(main_path)
    return output

def decide_what_hero_to_play():
    """

    :return:
    """
    while True:
        print("Do you want to create a new hero or to play an existing one?\nCreate(c)\nChoose existing hero(e)")
        option = input("\nChoose an option by writing his corresponding letter: ")
        if option == "e":
            return play_existing_hero()
        elif option == "c":
            return create_hero()
        else:
            print("\nthe letter you choose doesn't correspond to any option. Try again\n")

def read_inventory(file):
    """

    :param file:
    :return:
    """
    main_path = os.getcwd()
    os.chdir("Your_characters")
    with open(file, "r") as fr:
        content = fr.readlines()
    os.chdir(main_path)
    inventory = [item.strip() for item in content]
    return inventory[0:31]

def get_random_rarity(difficulty):
    """

    :param difficulty:
    :return:
    """
    red_item = 10 * difficulty
    grren_item = 50 * difficulty
    yellow_item = 500 * difficulty
    blue_item = 1300 * difficulty
    black_chance = 10000 - blue_item - yellow_item - grren_item - red_item
    list_chances = ["common"] * (black_chance) + ["normal"] * (blue_item) + ["rare"] * \
                   (yellow_item) + ["legendary"] * (grren_item) + ["angelic"] * (red_item)
    return random.choice(list_chances)

def get_rarity_color(rarity):
    if rarity == "common":
        return "\033[40m", "\033[30m"
    elif rarity == "normal":
        return "\033[44m", "\033[34m"
    elif rarity == "rare":
        return "\033[43m", "\033[33m"
    elif rarity == "legendary":
        return "\033[42m", "\033[32m"
    elif rarity == "angelic":
        return "\033[41m", "\033[31m"

def print_inventory(file):
    """

    :param file:
    :return:
    """

    symbol = get_hero_symbol(file[0:-4])
    inventory = read_inventory(file)
    print(f"\033[4m{' ' * 30}\033[0;0m ")
    print(f"|\033[95m{file[0:-4]}  lv: {inventory[1]}\033[0;0m "
          f"{' ' * (21 - len(file[0:-4]) - len(str(inventory[1])))}|")
    print(f"|  \033[1;30;45m|{symbol}|\033[2;37;0m{' ' * 10}\033[1;30;47m{item_colored_symbol(inventory[12])}\033[0;0m "
          f"\033[1;30;47m{item_colored_symbol(inventory[7])}\033[2;37;0m{' ' * 7}|")
    print(f"|\033[32mwood: {inventory[3]}\033[0;0m{' ' * (22 - len(str(inventory[3])))}|")
    print(f"|\033[38msilver: {inventory[4]}\033[0;0m"
          f"{' ' * (8 - len(str(inventory[4])))}\033[1;30;47m{item_colored_symbol(inventory[10])}\033[0;0m "
          f"\033[1;30;47m{item_colored_symbol(inventory[8])}\033[0;0m \033[1;30;47m{item_colored_symbol(inventory[11])}\033[0;0m"
          f"{' ' * 3}|")
    print(f"|\033[34mdiamond: {inventory[5]}\033[0;0m{' ' * (19- len(str(inventory[5])))}|")
    print(f"|\033[31mangelic dust: {inventory[6]}\033[0;0m{' ' * (5- len(str(inventory[6])))}\033[1;30;47m"
          f"{item_colored_symbol(inventory[9])}\033[0;0m"
          f"{' ' * 7}|")
    print(f"|\033[33mgold: {inventory[2]}\033[0;0m{' ' * (22- len(str(inventory[2])))}|")
    row_1 = ""
    row_2 = ""
    row_3 = ""
    row_counter = 1
    for item in inventory[13::]:
        if row_counter <= 6:
            row_1 += f"{item_colored_symbol(item)}  "
        elif row_counter <= 12:
            row_2 += f"{item_colored_symbol(item)}  "
        elif row_counter <= 18:
            row_3 += f"{item_colored_symbol(item)}  "
        row_counter += 1
    if row_counter < 7:
        row_1 += "\033[1;30;47m  \033[0;0m  " * (7 - row_counter)
        row_2 += "\033[1;30;47m  \033[0;0m  " * 6
        row_3 += "\033[1;30;47m  \033[0;0m  " * 6
    elif row_counter < 13:
        row_2 += "\033[1;30;47m  \033[0;0m  " * (13 - row_counter)
        row_3 += "\033[1;30;47m  \033[0;0m  " * 6
    elif row_counter < 19:
        row_3 += "\033[1;30;47m  \033[0;0m  " * (19 - row_counter)
    print(f"|{' ' * 28}|")
    print(f"|  {row_1} |")
    print(f"|{' ' * 28}|")
    print(f"|  {row_2} |")
    print(f"|{' ' * 28}|")
    print(f"|  {row_3} |")
    print(f"|\033[4m{' ' * 28}\033[0;0m|")

def random_primary_stat(rarity,):
    """

    :return:
    """

    if rarity == "common":
        return f"primary: {random.choice(range(2, 5))}"
    elif rarity == "normal":
        return f"primary: {random.choice(range(4, 12))}"
    elif rarity == "rare":
        return f"primary: {random.choice(range(10, 25))}"
    elif rarity == "legendary":
        return f"primary: {random.choice(range(50, 100))}"
    elif rarity == "angelic":
        return f"primary: 200"

def random_secondary_stat(rarity):
    """

    :param rarity:
    :return:
    """
    stat_factor = {"armor": 1, "hp": 10, "crit": 0.2, "crit_dmg": 1, "speed": 1, "mr": 1,
                   "dodge": 0.4, "cc_immun": 0.2, "mana": 0.2}

    stat_name = random.choice(list(stat_factor.keys()))

    if rarity == "common":
        return f"{stat_name}: {round(random.choice(range(2, 5)) * stat_factor[stat_name], 1)}"
    elif rarity == "normal":
        return f"{stat_name}: {round(random.choice(range(4, 12)) * stat_factor[stat_name], 1)}"
    elif rarity == "rare":
        return f"{stat_name}: {round(random.choice(range(10, 25)) * stat_factor[stat_name], 1)}"
    elif rarity == "legendary":
        return f"{stat_name}: {round(random.choice(range(50, 100)) * stat_factor[stat_name], 1)}"
    elif rarity == "angelic":
        return f"{stat_name}: {round(200 * stat_factor[stat_name], 1)}"

def create_item(item_name, difficulty):
    """

    :param item_name:
    :param difficulty:
    :return:
    """
    rarity = get_random_rarity(difficulty)
    return f"{item_name} {rarity} {random_primary_stat(rarity)}" \
           f" {random_secondary_stat(rarity)} {random_secondary_stat(rarity)} {random_secondary_stat(rarity)}"\

def print_item(item):
    """

    :param item:
    :return:
    """
    item_l = item.split()

    item_color, text_color = get_rarity_color(item_l[1])
    print(f" {text_color}{'_' * 16}\033[0;0m ")
    print(f"{text_color}|\033[0;0m{item_l[0]}{' ' * (15 - len(item_l[0]) - len(item_l[1]))}{text_color}{item_l[1]} |")
    print(f"{text_color}|\033[0;0m {item_color}{get_item_symbol(item_l[0]) }\033[0;0m{' ' * 13}{text_color}|\033[0;0m")
    print(f"{text_color}|{' ' * 16}|\033[0;0m")
    print(f"{text_color}|\033[0;0m {item_l[2]} {item_l[3]}\033[0;0m{' ' * (14 - len(item_l[2]) - len(item_l[3]))}{text_color}|\033[0;0m")
    print(f"{text_color}|\033[0;0m {item_l[4]} {item_l[5]}\033[0;0m{' ' * (14 - len(item_l[4]) - len(item_l[5]))}{text_color}|\033[0;0m")
    print(f"{text_color}|\033[0;0m {item_l[6]} {item_l[7]}\033[0;0m{' ' * (14 - len(item_l[6]) - len(item_l[7]))}{text_color}|\033[0;0m")
    print(f"{text_color}|\033[0;0m {item_l[8]} {item_l[9]}\033[0;0m{' ' * (14 - len(item_l[8]) - len(item_l[9]))}{text_color}|\033[0;0m")
    print(f"{text_color}|{'_' * 16}|\033[0;0m")

def item_colored_symbol(item):
    """

    :param item:
    :return:
    """
    if item == "empty":
        return "\033[1;30;47m⬛\033[0;0m"
    else:
        item_l = item.split()
        item_color, text_color = get_rarity_color(item_l[1])
        return f"{item_color}{get_item_symbol(item_l[0])}\033[0;0m"

def navigate_inventory(file):
    """

    :return:
    """
    full_inventory = read_inventory(file)
    inventory = full_inventory[7::]

    print("inventory option:\n-see stats of a weapon(s)\n-equip a weapon(e)\ngo back(b)")
    option = input("choose one option by writing its corresponding value:")
    while True:
        if option == "s":
            print("Choose item:\nhead(0)\nchest(1)\nfeet(2)\nmain hand(3)\n2nd hand(4)\n"
                  "jewel(5)\ninventory(coresponding number)")
            option_s = get_correct_int_input("choose one option by writing its corresponding value:")
            if option_s <= 21:
                if inventory[option_s] == "empty":
                    print("You don't have an item in that slot")
                else:
                    print_item(inventory[option_s])
            else:
                print("Wrong value entered")
        elif option == "b":
            break
        else:
            print("wrong value entered")
        print("inventory option:\n-see stats of a weapon(s)\n-equip a weapon(e)\ngo back(b)")
        option = input("choose one option by writing its corresponding value:")

def hero_movement(file, column: int, row: int, max_column: int, max_row: int, room):
    """

    :param column:
    :param row:
    :param max_column:
    :param max_row:
    :return:
    """
    print("You can move by entering a sequence of movements:\n-go up(w)\n-go left(a)\n-go down(s)\n-go right(d)\n\n"
          "Other options:\nsee inventory(i)\nexit(e)")
    movement = str.lower(input("enter a sequence of movements:"))
    correct_movement = 0
    while True:
        if movement == "i":
            print_inventory(file)
            navigate_inventory(file)
            return column, row
        elif movement == "e":
            print("Are you sure you want to exit?")
            if get_answer():
                return 0, 0
        else:
            row_func = row
            column_func = column
            while True:
                for ch in movement:
                    if ch not in "wasd":
                        print("incorrect sequence")
                        break
                else:
                    for ch in movement:
                        if ch == "w":
                            row_func -= 1
                        if ch == "s":
                            row_func += 1
                        if ch == "a":
                            column_func -= 1
                        if ch == "d":
                            column_func += 1
                if 0 < column_func <= max_column and 0 < row_func <= max_row:
                    print(f"Your room: {room}")
                    print(f"column:{column_func}   row:{row_func}")
                    return column_func, row_func
                else:
                    print("incorrect movement")
                    break
        movement = str.lower(input("enter a sequence of movements:"))

def village():
    """

    :return:
    """
    return ["", f"{'  ' * 2}⚒{'  ' * 3}‼", f"{'  ' * 7}", f"{'  ' * 3}⛲{'  ' * 3}", f"{'  ' * 7}", f"💍{'  ' * 2}💰{'  ' * 2}📕"]

def print_village(village):
    margin = f"\033[48;5;0m{' ' * 2}\033[0;0m"
    print(f"\033[48;5;0m{' ' * 22}\033[0;0m")
    print(f"\033[48;5;0m{'  ' * 2}\033[48;5;28m🏘🏠⛪\033[48;5;0m{'  ' * 2}\033[48;5;28m🏚🏤\033[48;5;0m   \033[0;0m")

    print(f"{margin}{margin}\033[48;5;28m{village[1]}⛪{margin}")
    print(f"{margin}{margin}\033[48;5;28m{village[2]}{margin}{margin}")
    print(f"{margin}{margin}\033[48;5;28m{village[3]}{margin}{margin}")
    print(f"{margin}\033[48;5;28m🏰{village[4]}{margin}{margin}")
    print(f"{margin}\033[48;5;28m💒{village[5]}🏚{margin}")
    print(f"\033[48;5;0m{'  ' * 1}\033[48;5;28m🏘🏠\033[48;5;0m{'  ' * 2}\033[48;5;28m🏚🏤\033[48;5;0m {'  ' * 3}\033[0;0m")
    print(f"\033[48;5;0m{' ' * 22}\033[0;0m")

def village_interface(name):

    column_v, row_v = 1, 3
    print(f"Move to the book(📕) if you want to read the Tutorial, and the rules")
    village_with_hero = add_object_on_map(village(), get_hero_symbol(name), column_v, row_v)
    print_village(village_with_hero)
    while True:
        column_v, row_v = hero_movement("ramulica.txt", column_v, row_v, 7, 5, "Village")
        if column_v == 0 and row_v == 0:
            return False
        else:
            village_with_hero = add_object_on_map(village(), get_hero_symbol(name), column_v, row_v)
            print_village(village_with_hero)
            if column_v == 7 and row_v == 1:
                print("Do you want to go in a mission?")
                if get_answer():
                    return True
            elif column_v == 1 and row_v == 5:
                print("Do you want to buy something from jeweler?")
                if get_answer():
                    print("make jeweler interface")
            elif column_v == 4 and row_v == 5:
                print("Do you want to buy something from shop?")
                if get_answer():
                    print("make shop interface")
            elif column_v == 7 and row_v == 5:
                print("Do you want to read the tutorial?")
                if get_answer():
                    print("Tutorial")
            elif column_v == 3 and row_v == 1:
                print("Do you want to craft something at the blacksmith?")
                if get_answer():
                    print("make blacksmith interface")

def margin_random_generator(column, row):
    cl_rw = ["x", "y"]
    random.shuffle(cl_rw)
    if cl_rw[0] == "x":
        return [random.choice([column, 1]), random.choice(range(1, row))]
    else:
        return [random.choice(range(1, column)), random.choice([row, 1])]

def door_data(entrance, biome, column, row, next_room):
    if entrance == "up":
        return random.choice(range(2, column - 1)), 1, next_room, choose_door(biome)
    elif entrance == "down":
        return random.choice(range(2, column - 1)), row, next_room, choose_door(biome)
    elif entrance == "right":
        return column, random.choice(range(2, row - 1)), next_room, choose_door(biome)
    elif entrance == "left":
        return 1, random.choice(range(2, row - 1)), next_room, choose_door(biome)

class Map:

    def __init__(self, room, entrance, exit, length, height):
        self.entrance = entrance
        room_m = add_object_on_map(room, self.entrance[3], self.entrance[0], self.entrance[1])

        self.exit = exit
        room_m = add_object_on_map(room_m, self.exit[3], self.exit[0], self.exit[1])

        list_of_enemies = []

        self.enemy_1 = margin_random_generator(length, height), choose_enemy()
        list_of_enemies.append(self.enemy_1)

        self.enemy_2 = margin_random_generator(length, height), choose_enemy()
        list_of_enemies.append(self.enemy_2)

        self.enemy_3 = margin_random_generator(length, height), choose_enemy()
        list_of_enemies.append(self.enemy_3)

        if random.choice([True, False]):
            self.enemy_4 = [random.choice(range(1, length)), random.choice(range(1, height))], choose_enemy()
            list_of_enemies.append(self.enemy_4)

        if random.choice([True, False]):
            self.enemy_5 = [random.choice(range(1, length)), random.choice(range(1, height))], choose_enemy()
            list_of_enemies.append(self.enemy_5)

        if random.choice([True, False]):
            self.enemy_6 = [random.choice(range(1, length)), random.choice(range(1, height))], choose_enemy()
            list_of_enemies.append(self.enemy_6)

        self.room = room_m
        self.list_of_enemies = list_of_enemies

        self.dead_enemies = []

    def room_final(self):
        room_e = self.room
        for item in self.list_of_enemies:
            if item[0] not in self.dead_enemies:
                room_e = add_object_on_map(room_e, item[1], item[0][0], item[0][1])
        return room_e

def add_random_doors():
    door_wall = ["up", "down"]
    for _ in range(5):
        if door_wall[-1] == "down":
            door_wall.extend(random.choice([['up', 'down'], ['right', 'left'], ['left', 'right']]))
        elif door_wall[-1] == "up":
            door_wall.extend(random.choice([['down', 'up'], ['right', 'left'], ['left', 'right']]))
        elif door_wall[-1] == "left":
            door_wall.extend(random.choice([['down', 'up'], ['right', 'left'], ['up', 'down']]))
        elif door_wall[-1] == "right":
            door_wall.extend(random.choice([['down', 'up'], ['up', 'down'], ['left', 'right']]))
    return door_wall[1::]

def generate_arena(player, enemies):
    arena = [f"{' '* 54}"] * 15
    column_row_l = [[14, 12], [10, 11], [18, 11], [6, 10], [22, 10], [2, 9], [26, 9]]
    effects_coordinates = [[0, 1], [-1, 1], [1, 1], [0, 2], [-1, 2], [1, 2]]
    for i, item in enumerate(player):
        for i_1, effect in enumerate(item[1::]):
            arena = add_object_on_map(arena, effect, column_row_l[i][0] + effects_coordinates[i_1][0],
                                      column_row_l[i][1] - effects_coordinates[i_1][1])

        arena = add_object_on_map(arena, player[i][0], column_row_l[i][0], column_row_l[i][1])
    for i, item in enumerate(enemies):
        for i_1, effect in enumerate(item[1::]):
            arena = add_object_on_map(arena, effect, column_row_l[i][0] + effects_coordinates[i_1][0],
                                      column_row_l[i][1] - effects_coordinates[i_1][1] - 6)
        arena = add_object_on_map(arena, enemies[i][0], column_row_l[i][0], column_row_l[i][1] - 6)
    return arena
"""
player: [[][]]
"""
def ordonate_hp(entities):
    coordinates = [7, 5, 9, 3, 11, 1, 13]
    life_row = ["  ", "      "] * 7
    life_str = ""
    for i in range(len(entities)):

        life_row[coordinates[i]] = str(int(entities[i].life[0])).zfill(6)
    for item in life_row:
        life_str += item
    return life_str

def printe_arena(arena, ally_l, enemy_l):
    margin = f"\033[48;5;16m  \033[0;0m"
    print(f"\033[38;5;34m{ordonate_hp(enemy_l)}\033[0;0m")
    print(f"{margin}\033[48;5;16m{arena[0]}\033[0;0m{margin}")
    for item in arena[1:-1]:
        line = ""
        for ch in item:
            if ch != " ":
                line += f"\033[48;5;246m{ch}\033[48;5;236m"
            else:
                line += ch
        print(f"{margin}\033[48;5;236m{line}\033[0;0m{margin}")
    print(f"{margin}\033[48;5;16m{arena[-1]}\033[0;0m{margin}")
    print(f"\033[38;5;34m{ordonate_hp(ally_l)}\033[0;0m")

    print("\nabilities")
    print(ally_l[0].life, ally_l[0].mana)

def get_effect_symbol(effect):
    effects = {"freeze": "❄", "burn": "🔥", "bleed": "🩸", "blind": "✨", "stun": "💫", "sleep": "💤", "taunt": "💢", "charm": "💞"}
    for k, v in effects.items():
        if effect == k:
            return v

def stats_from_items(inventory):

    added_stats = {"primary": 0, "armor": 0, "hp": 0, "crit": 0, "crit_dmg": 0, "speed": 0, "mr": 0,
                   "dodge": 0, "cc_immun": 0, "mana": 0}
    for item in inventory[7:13]:
        item_stats = item.split("-")
        print(item_stats)
        for stat in item_stats[2::]:
            added_stats[stat[0:stat.find(":")]] += float(stat[stat.rfind(" ") + 1::])
    return added_stats

class Combatant_izoteric:
    def __init__(self, symbol, life, mana, data, good_bad_side, bonus = {"primary": 0, "armor": 0, "hp": 0, "crit": 0, "crit_dmg": 0, "speed": 0, "mr": 0,
                   "dodge": 0, "cc_immun": 0, "mana": 0}):
        self.symbol = symbol
        self.primary = data[0] + bonus["primary"]
        self.life = [life + bonus["hp"]] + [data[1] + bonus["hp"]]
        self.damage = data[2] + bonus["primary"]
        self.speed = data[3] + bonus["speed"]
        self.crit = data[4]  + bonus["crit"]
        self.crit_dmg = data[5] + bonus["crit_dmg"]
        self.armor = data[6] + bonus["armor"]
        self.mr = data[7] + bonus["mr"]
        self.dodge = data[8] + bonus["dodge"]
        self.cc_immun = data[9] + bonus["cc_immun"]
        self.mana = [mana + bonus["mana"]] + [data[10] + bonus["mana"]]
        self.side = good_bad_side
        self.effects = {}

    def deal_damage_heal(self, damage):
        if self.life[0] + damage <= self.life[1]:
            self.life = [self.life[0] + damage] + [self.life[1]]
        else:
            self.life = [self.life[1]] + [self.life[1]]

    def get_lose_mana(self, mana_difference):
        if self.mana[0] + mana_difference <= self.mana[1]:
            self.mana = [self.mana[0] + mana_difference] + [self.mana[1]]
        else:
            self.mana = [self.mana[1]] + [self.mana[1]]

    def round_end_effect(self):
        output = {}
        if 'burn' in self.effects:
            Combatant_izoteric.deal_damage_heal(self, -self.damage)
        if 'bleed' in self.effects:
            Combatant_izoteric.deal_damage_heal(self, -(self.life[0] * 0.1))
        for k, v in self.effects.items():
            if v > 0:
                output[k] = v - 1
        self.effects = output

    def printable_symbols(self):
        return [self.symbol] + [get_effect_symbol(k) for k, v in self.effects.items()]

    def crit_mechanic(self):
        if self.crit >= 100:
            return self.damage + (self.damage * (self.crit_dmg + self.crit - 100) / 100)
        else:
            x = random.choice([1] * int(self.crit) + [0] * (100 - int(self.crit)))
            if x == 1:
                return self.damage + (self.damage * self.crit_dmg / 100)
            else:
                return self.damage

    def basic_attack_class(self, hero_class, enemies_l):
        output = []
        if hero_class == "wizard":
            Combatant_izoteric.get_lose_mana(self, 15)
            for _ in range(len(enemies_l)):
                output.append([Combatant_izoteric.crit_mechanic(self)/len(enemies_l), []])
        return output, "magical", "all"

    def first_ability(self, hero_class, enemies_l):
        output = []
        if hero_class == "wizard":
            if self.mana[0] >= 25:
                Combatant_izoteric.get_lose_mana(self, -25)
                for _ in range(len(enemies_l)):
                    output.append([Combatant_izoteric.crit_mechanic(self), [f'{random.choice(["freeze"] * 70 + ["none"] * 30)}2']])
            else:
                print("not enough mana")
                return 'error'

        return output, "magical", "damage"

    def second_ability(self, hero_class, enemies_l):
        output = []
        if hero_class == "wizard":
            if self.mana[0] >= 35:
                Combatant_izoteric.get_lose_mana(self, -35)
                try:
                    attack_eneies = random.sample(range(len(enemies_l)), 2)
                except:
                    attack_eneies = [0]
                for i in range(len(enemies_l)):
                    if i in attack_eneies:
                        if "freeze" in [item[0:-1] for item in enemies_l[i]]:
                            output.append([Combatant_izoteric.crit_mechanic(self) * 4, ["burn3"]])
                        else:
                            output.append([Combatant_izoteric.crit_mechanic(self) * 2, ["burn3"]])
                    else:
                        output.append([0, []])
            else:
                print("not enough mana")
                return 'error'

        return output, "magical", "damage"

    def third_ability(self, hero_class, enemies_effects_l):
        output = []
        if hero_class == "wizard":
            if self.mana[0] == self.mana[1]:
                Combatant_izoteric.get_lose_mana(self, self.mana[1])
                for item in enemies_effects_l:
                    new_effects = []
                    for effect in item:
                        if effect[0:-1] == "freeze":
                            new_effects.append("freeze2")
                        elif effect[0:-1] == "burn":
                            new_effects.append("burn3")
                        else:
                            new_effects.append(effect)
                    output.append([0, new_effects])
                return output, "magical", "round"
            else:
                print("Not enough mana")
                return 'error'

    def forth_ability(self, hero_class, enemies_l):
        output = []
        if hero_class == "wizard":
            if self.mana[0] >= 50:
                Combatant_izoteric.get_lose_mana(self, -50)
                attack_enemy = random.choice(range(len(enemies_l)))

                for i in range(len(enemies_l)):
                    if i == attack_enemy:
                        if "burn" in [item[0:-1] for item in enemies_l[i]]:
                            Combatant_izoteric.deal_damage_heal(self, int(self.damage * 15 * 0.1))
                        output.append([Combatant_izoteric.crit_mechanic(self) * 15, ["blind2"]])
                    else:
                        output.append([0, []])
            else:
                print("not enough mana")
                return 'error'

        return output, "pure", "damage"

    def use_ability(self, hero_class, enemiy_l):
        option = input("choose ability")
        while True:
            if option == "a": #  and "blind" not in self.effects:
                ability_0 = Combatant_izoteric.basic_attack_class(self, hero_class, enemiy_l)
                if ability_0 != "error":
                    return ability_0

            elif option == "q":
                ability_1 = Combatant_izoteric.first_ability(self, hero_class, enemiy_l)
                if ability_1 != "error":
                    return ability_1
                else:
                    print('\033[38;5;160mYou do not have enough mana\033[0;0m')
                    option = input("choose ability")

            elif option == "w":
                ability_2 = Combatant_izoteric.second_ability(self, hero_class, enemiy_l)
                if ability_2 != "error":
                    return ability_2
                else:
                    print('\033[38;5;160mYou do not have enough mana\033[0;0m')
                    option = input("choose ability")

            elif option == "e":
                ability_3 = Combatant_izoteric.third_ability(self, hero_class, enemiy_l)
                if ability_3 != "error":
                    return ability_3
                else:
                    print('\033[38;5;160mYou do not have enough mana\033[0;0m')
                    option = input("choose ability")

            elif option == "r":
                ability_4 = Combatant_izoteric.forth_ability(self, hero_class, enemiy_l)
                if ability_4 != "error":
                    return ability_4
                else:
                    print('\033[38;5;160mYou do not have enough mana\033[0;0m')
                    option = input("choose ability")

            else:
                option = input("choose ability")

    def enemy_attack(self, allies):
        output = []
        if self.mana[0] == self.mana[1]:
            for i in range(len(allies)):

                output.append([Combatant_izoteric.crit_mechanic(self) * 1.2,
                               [f"{random.choice(['stun'] * 5 + ['burn'] * 5 + ['blind'] * 5 + ['none'] * 85)}2"]])

        else:

            Combatant_izoteric.get_lose_mana(self, 15)
            attack_ally = random.choice(range(len(allies)))

            for i in range(len(allies)):
                if i == attack_ally:
                    output.append([Combatant_izoteric.crit_mechanic(self) * 1.2,
                                   [f"{random.choice(['stun'] * 5 + ['burn'] * 5 + ['blind'] * 5 + ['none'] * 85)}2"]])
                else:
                    output.append([0, []])
        return output, "pure", "damage"

    def list_of_effects(self):
        output = []
        for k, v in self.effects.items():
            output.append(f"{k}{v}")
        return output

def ability_effect(enemy_list, damage_effects):
    if damage_effects[1] == "magical":
        for i, item in enumerate(enemy_list):
            damage = damage_effects[0][i][0] - int(item.mr)

            Combatant_izoteric.deal_damage_heal(item, -damage)
            for effect in damage_effects[0][i][1]:
                if effect[0:-1] != "none":
                    item.effects[effect[0:-1]] = int(effect[-1])
    elif damage_effects[1] == "pure":
        for i, item in enumerate(enemy_list):
            damage = damage_effects[0][i][0]
            Combatant_izoteric.deal_damage_heal(item, -damage)
            for effect in damage_effects[0][i][1]:
                if effect[0:-1] != "none":
                    item.effects[effect[0:-1]] = int(effect[-1])
    elif damage_effects[1] == "error":
        return "again"

def remove_dead_body(enemy_list):
    for item in enemy_list:
        if item.life[0] <= 0:
            enemy_list.remove(item)
    return enemy_list

def enter_a_turn(meesage):
    x = input(meesage)
    while True:
        if x == "c":
            break
        else:
            print("You entered a wrong answer")
            x = input(meesage)
    return True

hero_file = decide_what_hero_to_play()
hero_name = hero_file[0:hero_file.rfind(".")]
hero_inventory = read_inventory(hero_file)

door_map_concatenation = add_random_doors()
biome = get_random_biome()
length_1, height_1 = generate_room_length_height()
length_2, height_2 = generate_room_length_height()
length_3, height_3 = generate_room_length_height()
length_4, height_4 = generate_room_length_height()
length_5, height_5 = generate_room_length_height()
length_6, height_6 = generate_room_length_height()

length_list = [length_1, length_2, length_3, length_4, length_5, length_6]
height_list = [height_1, height_2, height_3, height_4, height_5, height_6]

map_layout = [Map(generate_rooms(biome, length_1, height_1),
                  door_data(door_map_concatenation[0], biome, length_1, height_1, "Village"),
                  door_data(door_map_concatenation[1], biome, length_1, height_1, 1), length_1, height_1),
              Map(generate_rooms(biome, length_2, height_2),
                  door_data(door_map_concatenation[2], biome, length_2, height_2, 0),
                  door_data(door_map_concatenation[3], biome, length_2, height_2, 2), length_2, height_2),
              Map(generate_rooms(biome, length_3, height_3),
                  door_data(door_map_concatenation[4], biome, length_3, height_3, 1),
                  door_data(door_map_concatenation[5], biome, length_3, height_3, 3), length_3, height_3),
              Map(generate_rooms(biome, length_4, height_4),
                  door_data(door_map_concatenation[6], biome, length_4, height_4, 2),
                  door_data(door_map_concatenation[7], biome, length_4, height_4, 4), length_4, height_4),
              Map(generate_rooms(biome, length_5, height_5),
                  door_data(door_map_concatenation[6], biome, length_5, height_5, 3),
                  door_data(door_map_concatenation[7], biome, length_5, height_5, 5), length_5, height_5),
              Map(generate_rooms(biome, length_6, height_6),
                  door_data(door_map_concatenation[6], biome, length_6, height_6, 4),
                  door_data(door_map_concatenation[7], biome, length_6, height_6, 6), length_6, height_6)
              ]

room_index = 0
while village_interface(hero_name):
    print("Do you want to continue on the same map or not?")
    if not get_answer():
        door_map_concatenation = add_random_doors()
        biome = get_random_biome()
        length_1, height_1 = generate_room_length_height()
        length_2, height_2 = generate_room_length_height()
        length_3, height_3 = generate_room_length_height()
        length_4, height_4 = generate_room_length_height()
        length_5, height_5 = generate_room_length_height()
        length_6, height_6 = generate_room_length_height()

        length_list = [length_1, length_2, length_3, length_4, length_5, length_6]
        height_list = [height_1, height_2, height_3, height_4, height_5, height_6]

        map_layout = [Map(generate_rooms(biome, length_1, height_1),
                          door_data(door_map_concatenation[0], biome, length_1, height_1, "Village"),
                          door_data(door_map_concatenation[1], biome, length_1, height_1, 1), length_1, height_1),
                      Map(generate_rooms(biome, length_2, height_2),
                          door_data(door_map_concatenation[2], biome, length_2, height_2, 0),
                          door_data(door_map_concatenation[3], biome, length_2, height_2, 2), length_2, height_2),
                      Map(generate_rooms(biome, length_3, height_3),
                          door_data(door_map_concatenation[4], biome, length_3, height_3, 1),
                          door_data(door_map_concatenation[5], biome, length_3, height_3, 3), length_3, height_3),
                      Map(generate_rooms(biome, length_4, height_4),
                          door_data(door_map_concatenation[6], biome, length_4, height_4, 2),
                          door_data(door_map_concatenation[7], biome, length_4, height_4, 4), length_4, height_4),
                      Map(generate_rooms(biome, length_5, height_5),
                          door_data(door_map_concatenation[6], biome, length_5, height_5, 3),
                          door_data(door_map_concatenation[7], biome, length_5, height_5, 5), length_5, height_5),
                      Map(generate_rooms(biome, length_6, height_6),
                          door_data(door_map_concatenation[6], biome, length_6, height_6, 4),
                          door_data(door_map_concatenation[7], biome, length_6, height_6, 6), length_6, height_6)
                      ]

    column, row = map_layout[room_index].entrance[0], map_layout[room_index].entrance[1]
    room_1_h = add_object_on_map(Map.room_final(map_layout[room_index]), get_hero_symbol(hero_name), column, row)
    print_room(room_1_h, biome)
    while True:
        column, row = hero_movement(hero_file, column, row, length_list[room_index], height_list[room_index], room_index)
        if column + row == 0:
            break
        for i, item in enumerate(map_layout[room_index].list_of_enemies):
            if column == item[0][0] and row == item[0][1] and item[0] not in map_layout[room_index].dead_enemies:
                room_1_h = add_object_on_map(Map.room_final(map_layout[room_index]), get_hero_symbol(hero_name), column, row)
                print_room(room_1_h, biome)
                print(f"You've encountered an enemy {item[1]}. Do you want to fight it?")
                if get_answer():
                    difficulty = 1

                    enemies_len = random.choice(range(1, 7))
                    enemy_l = [Combatant_izoteric(item[1], 90000 / enemies_len, 0,
                                                  [40 / enemies_len, 90000 / enemies_len, 45 / enemies_len, 200, 0, 0,
                                                   3, 5, 0, 0, 45], "evil") for i in range(enemies_len)]

                    ally_l = []
                    enemy_2 = Combatant_izoteric("🧙", 850, 70, [40, 850, 45, 200, 0, 0, 3, 5, 0, 0, 70], "good",
                                                 stats_from_items(read_inventory("ramulica.txt")))
                    print(enemy_2.primary)
                    ally_l.append(enemy_2)

                    print(enemy_2.mana)

                    list_of_e = [Combatant_izoteric.list_of_effects(item_f) for item_f in enemy_l]
                    printe_arena(generate_arena([Combatant_izoteric.printable_symbols(enemy_2)],
                                                [Combatant_izoteric.printable_symbols(item_f) for item_f in enemy_l]),
                                 ally_l, enemy_l)
                    outcome = "Lose"
                    turn = 0
                    while True:
                        turn += 1
                        print(f"\n\033[38;5;220mIt's turn {turn}\033[0;0m\n")
                        if "freeze" in enemy_2.effects or "stun" in enemy_2.effects:
                            print(f"you are unable to attack because of your condition{enemy_2.effects}")
                        else:
                            if enter_a_turn("It's your turn to attack, enter c to continue"):

                                damage_u_deal = Combatant_izoteric.use_ability(enemy_2, 'wizard', list_of_e)
                                if damage_u_deal != "again":
                                    ability_effect(enemy_l, damage_u_deal)
                                    print(
                                        f"\n\033[38;5;27mYour mana pool is {enemy_2.mana[0]}/{enemy_2.mana[1]}\033[0;0m")
                                    print(f"You did \033[38;5;196m{damage_u_deal} damage\n\033[0;0m")

                        enemy_l = remove_dead_body(enemy_l)
                        list_of_e = [Combatant_izoteric.list_of_effects(item_f) for item_f in enemy_l]
                        printe_arena(generate_arena([Combatant_izoteric.printable_symbols(enemy_2)],
                                                    [Combatant_izoteric.printable_symbols(item_f) for item_f in
                                                     enemy_l]), ally_l, enemy_l)

                        if len(enemy_l) == 0:
                            outcome = "Victory"
                            break

                        if enter_a_turn("It's enemy's turn to attack, enter c to continue"):

                            for i, item_f in enumerate(enemy_l):
                                if "freeze" in item_f.effects or "stun" in item_f.effects:
                                    print(f"enemy {i + 1} is unable to attack because of his condition{item_f.effects}")
                                else:
                                    damage_dealt_by_enemy = Combatant_izoteric.enemy_attack(item_f, [enemy_2])
                                    ability_effect([enemy_2], damage_dealt_by_enemy)
                                    print(
                                        f"Enemy {i + 1} did \033[38;5;196m{damage_dealt_by_enemy} damage\033[0;0m to you")

                        printe_arena(generate_arena([Combatant_izoteric.printable_symbols(enemy_2)],
                                                    [Combatant_izoteric.printable_symbols(item_f) for item_f in
                                                     enemy_l]), ally_l, enemy_l)

                        for item_f in enemy_l:
                            Combatant_izoteric.round_end_effect(item_f)
                        for item_f in ally_l:
                            Combatant_izoteric.round_end_effect(item_f)
                    if outcome == "Victory":
                        print("victory")
                        map_layout[room_index].dead_enemies.append(item[0])
                break
        if column == map_layout[room_index].exit[0] and row == map_layout[room_index].exit[1]:
            room_1_h = add_object_on_map(Map.room_final(map_layout[room_index]), get_hero_symbol(hero_name), column, row)
            print_room(room_1_h, biome)
            print(room_index)
            print(f"Do you want to go to room {map_layout[room_index].exit[2]}")
            if get_answer():
                room_index = map_layout[room_index].exit[2]
                column, row = map_layout[room_index].entrance[0], map_layout[room_index].entrance[1]
        elif column == map_layout[room_index].entrance[0] and row == map_layout[room_index].entrance[1]:
            room_1_h = add_object_on_map(Map.room_final(map_layout[room_index]), get_hero_symbol(hero_name), column, row)
            print_room(room_1_h, biome)
            print(f"Do you want to go to room {map_layout[room_index].entrance[2]}")
            if get_answer():
                if map_layout[room_index].entrance[2] == "Village":
                    break
                else:
                    room_index = map_layout[room_index].entrance[2]
                    column, row = map_layout[room_index].exit[0], map_layout[room_index].exit[1]

        room_1_h = add_object_on_map(Map.room_final(map_layout[room_index]), get_hero_symbol(hero_name), column, row)
        print_room(room_1_h, biome)


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

'''translate.translate: provides main() entry point.'''

__version__ = '0.1.3'

import logging
import argparse

import requests
from bs4 import BeautifulSoup
from terminaltables import AsciiTable

logging.basicConfig(
        filename = '.log',
        filemode = 'a+',
        level    = logging.INFO,
        format   = '%(asctime)s | %(levelname)s | %(message)s',
        datefmt  = '%m/%d/%Y %H:%M:%S'
        )

def main():
    ''' Parse the arguments and start running what needs to be running '''

    parser = argparse.ArgumentParser()
    parser.add_argument(
            'dictionary', nargs='?', type=str, default='',
            help='Dictionary to use for translation. To translate from english to french, it should take the value enfr, for english to italian, enit, etc.'
            )
    parser.add_argument(
            'word', nargs='?', type=str, default='',
            help='Word to be translated'
            )
    parser.add_argument(
            '-l', '--list', action='store_true',
            help='Returns the list of available dictionaries.'
            )
    args = parser.parse_args()
    logging.info('Arguments parsed')

    dictionaries = get_dictionaries()
    if args.list:
        logging.info('Attempting to print the list of available dictionaries')
        print('')
        print('**** Available dictionaries:')
        print(dictionaries.table)
        logging.info('Printed the list of available dictionaries')

    if args.word and args.dictionary:
        translate_word(args.dictionary, args.word)
    else:
        if not args.list:
            logging.info('User didn\'t pass the correct arguments. Displaying the help message and shutting down')
            print('Please enter a dictionary and a word.')
            print('\tEnter -l or --list to get a list of all available dictionaries.')
            print('Enter -h or --help for help.')

def get_dictionaries():
    '''
    Requests wordreference.com homepage and parse the list of availables
    dictionaries
    '''

    url = 'http://www.wordreference.com'
    logging.info('Requesting {} for parsing'.format(url))
    r = requests.get(url)
    if r.status_code != 200:
        logging.info('Request failed with status {}'.format(r.status_code))
        return -1
    logging.info('Request for {} successful'.format(url))

    logging.info('Attempting to parse the html and extract the list of dictionaries')
    soup = BeautifulSoup(r.content, 'html.parser')
    options = soup.find_all('option')
    dictionaries = [ ['Key', 'Dictionary'] ]
    dictionaries += [ [option['id'], option.get_text()] for option in options
            if option['id'][:2] != option['id'][2:4]      # No definition option
            and len(option['id']) == 4                 # No synonyms or conjugation option
            ]

    logging.info('List of dictionaries extracted')
    table = AsciiTable(dictionaries)
    return table

def translate_word(dictionary, word):
    '''
    Requests the page for the translation of "word" using the dictionary
    "dictionary".
    Print a formatted version of the response
    '''

    # Iniital checks
    if not isinstance(dictionary, str) or len(dictionary) != 4:
        raise TypeError('''The "dictionary" argument must be a string of length 4,
                with the first two letters being the acronym of the original
                language, and the last two letters, the acronym of the language
                you would like to translate to.''')
    if not isinstance(word, str):
        raise TypeError('The "word" argument must be a string (type {} passed)'.format(type(word)))

    # Building the url (and formatting it) and get the html from GET
    base_url = 'http://www.wordreference.com/'
    url = base_url + dictionary + '/' + word.replace(' ', '%20')

    logging.info('Requesting {} for parsing'.format(url))
    r = requests.get(url)
    if r.status_code != 200:
        logging.info('Request failed with status {}'.format(r.status_code))
        return -1
    logging.info('Request for {} successful'.format(url))

    # Parsing the html to extract the data
    # I kept it to what matters:
    #   * Original word/expression
    #   * Translation
    # Because who really cares if it is an intransitive verb or a noun?
    logging.info('Attempting to parse the html and extract the translations')
    soup = BeautifulSoup(r.content, 'html.parser')
    table_single_form = soup.find_all('table', {'class': 'WRD'})[0]
    try:
        data_single_form = parse_translation_table(table_single_form)
    except IndexError:
        logging.warning('The word passed doesn\'t have any translation')
        return -1

    logging.info('Translations extracted')
    # print the results in a pretty way
    print_results(word, data_single_form)

def parse_translation_table(table):
    '''
    Given the table of translations extracted with BeautifulSoup, returns
    a list of lists containing the various translations.
    '''

    data = [ ['Original Language', 'Translation'] ]

    rows = table.find_all('tr')
    for row in rows:
        cells = row.find_all('td')
        if len(cells) == 3:
            if cells[2].em is None:
                continue
            cells[2].em.decompose()
            if cells[0].get_text(strip=True) == '':
                data[-1][1] += u'\n{}'.format(cells[2].get_text())
            else:
                data += [[
                    cells[0].find('strong').get_text(),
                    cells[2].get_text()
                    ]]
    return data

def print_results(word, data_single_form):
    ''' Pretty print of the translation results '''

    print('')
    print('**** Translations for {}:'.format(word))
    print(AsciiTable(data_single_form).table)
    print('')


#!/usr/bin/python3

from main import Session, engine, User

local_session = Session(bind = engine)

#ascending
users = local_session.query(User).order_by(User.username).all()

for user in users:
    print(f"{user.username}")


from odoo import api, fields, models

class Note(models.Model):
	_inherit = 'note.note'

	@api.multi
	def act_back(self):
		if self._context.get('save_close'):
			return {'type': 'ir.actions.act_window_close'}
		return {'name': 'Notes',
				'type': 'ir.actions.act_window',
				'view_id': self.env.ref('note_quick.view_quick_note_kanban').id,
				'view_mode': 'kanban',
				'res_model': 'note.note',
				'target': 'new'}

	@api.model
	def act_create_one(self):
		return {'name': 'Create a new note',
				'type': 'ir.actions.act_window',
				'view_id': self.env.ref('note_quick.view_quick_note_form').id,
				'view_mode': 'form',
				'res_model': 'note.note',
				'target': 'new'}

	@api.multi
	def act_edit(self):
		return {'name': 'Edit Note',
				'type': 'ir.actions.act_window',
				'view_id': self.env.ref('note_quick.view_quick_note_form').id,
				'view_mode': 'form',
				'res_model': 'note.note',
				'res_id': self.id,
				'target': 'new'}


from sqlalchemy.exc import IntegrityError
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from app.core.repo.base import BaseSqlalchemyRepo
from app.core.exceptions.repo import RepoException
from .models import Review

class ReviewRepo(BaseSqlalchemyRepo):
    model = Review

    async def create(self, db_session, obj_in):
        """
            Create review
        """
        try:
            return await super().create(db_session, obj_in)
        except IntegrityError as e:
            raise RepoException("Review name must be unique", e)

    async def get_review_by_room_id(self, db_session, room_id):
        stmt = select(self.model).options(selectinload(self.model.user)).where(self.model.room_id == room_id)
        result = await db_session.execute(stmt)
        return result.scalars().all()

    async def get(self, db_session, id):
        """
            Get review by id
        """
        obj = await super().get(db_session, id)

        if not obj:
            raise RepoException("Review not found", None, status=404)
        return obj

    async def get_all(self, db_session):
        """
            Get all reviews
        """
        return await super().list(db_session)

    async def update(self, db_session, id, obj_in):
        """
            Update review by id
        """
        db_obj = await self.get(db_session, id)

        if not db_obj:
            raise RepoException("Review not found", None, status=404)

        try:
            return await super().update(db_session, db_obj, obj_in)
        except IntegrityError as e:
                raise RepoException("Review title must be unique", e)

    async def delete(self, db_session, id, user_id):
        """
            Delete review by id
        """
        await self.get(db_session, id=id)
        return await super().delete(db_session, id=id)


from smtplib import SMTP
from email.header import Header
from email.mime.text import MIMEText

def main():
    # 请自行修改下面的邮件发送者和接收者
    sender = 'sunhui@zhongfu.net'
    receivers = ['1161186762@qq.com', 'daersunhui@gmail.com']
    message = MIMEText('用Python发送邮件的示例代码.', 'plain', 'utf-8')
    message['From'] = Header('大锤', 'utf-8')
    message['To'] = Header('发放', 'utf-8')
    message['Subject'] = Header('示例代码实验邮件', 'utf-8')
    smtper = SMTP('smtp.zhongfu.net')
    print("qqq")
    # 请自行修改下面的登录口令
    smtper.login(sender, '密码')
    smtper.sendmail(sender, receivers, message.as_string())
    print('邮件发送完成!')

if __name__ == '__main__':
    main()


import os

from rebasehelper.types import Options
from rebasehelper.constants import CONFIG_PATH, CONFIG_FILENAME, CHANGES_PATCH
from rebasehelper.plugins.plugin_manager import plugin_manager

OPTIONS: Options = [
    # basic
    {
        "name": ["--version"],
        "default": False,
        "switch": True,
        "help": "show rebase-helper version and exit",
    },
    # output control
    {
        "name": ["-v", "--verbose"],
        "default": 0,
        "counter": True,
        "help": "be more verbose",
    },
    {
        "name": ["--color"],
        "choices": ["always", "never", "auto"],
        "default": "auto",
        "help": "colorize the output, defaults to %(default)s",
    },
    {
        "name": ["--background"],
        "choices": ["dark", "light", "auto"],
        "default": "auto",
        "help": "use color scheme for the given background, defaults to %(default)s",
    },
    {
        "name": ["--results-dir"],
        "help": "location where the rebase-helper-results directory will be created",
    },
    {
        "name": ["--workspace-dir"],
        "help": "location where the rebase-helper-workspace directory will be created",
    },
    # tool selection
    {
        "name": ["--buildtool"],
        "choices": plugin_manager.build_tools.get_all_plugins(),
        "available_choices": plugin_manager.build_tools.get_supported_plugins(),
        "default": plugin_manager.build_tools.get_default_plugins(True),
        "help": "build tool to use, defaults to %(default)s",
    },
    {
        "name": ["--srpm-buildtool"],
        "choices": plugin_manager.srpm_build_tools.get_all_plugins(),
        "available_choices": plugin_manager.srpm_build_tools.get_supported_plugins(),
        "default": plugin_manager.srpm_build_tools.get_default_plugins(True),
        "help": "SRPM build tool to use, defaults to %(default)s",
    },
    {
        "name": ["--pkgcomparetool"],
        "choices": plugin_manager.checkers.get_all_plugins(),
        "available_choices": plugin_manager.checkers.get_supported_plugins(),
        "nargs": "?",
        "const": [""],
        "default": plugin_manager.checkers.get_default_plugins(),
        "type": lambda s: s.split(','),
        "help": "set of tools to use for package comparison, defaults to "
                "%(default)s if available",
    },
    {
        "name": ["--outputtool"],
        "choices": plugin_manager.output_tools.get_all_plugins(),
        "available_choices": plugin_manager.output_tools.get_supported_plugins(),
        "default": plugin_manager.output_tools.get_default_plugins(True),
        "help": "tool to use for formatting rebase output, defaults to %(default)s",
    },
    {
        "name": ["--versioneer"],
        "choices": plugin_manager.versioneers.get_all_plugins(),
        "available_choices": plugin_manager.versioneers.get_supported_plugins(),
        "default": None,
        "help": "tool to use for determining latest upstream version",
    },
    # blacklists
    {
        "name": ["--versioneer-blacklist"],
        "choices": plugin_manager.versioneers.get_all_plugins(),
        "available_choices": plugin_manager.versioneers.get_supported_plugins(),
        "nargs": "?",
        "const": [""],
        "default": [],
        "type": lambda s: s.split(","),
        "help": "prevent specified versioneers from being run",
    },
    {
        "name": ["--spec-hook-blacklist"],
        "choices": plugin_manager.spec_hooks.get_all_plugins(),
        "available_choices": plugin_manager.spec_hooks.get_supported_plugins(),
        "nargs": "?",
        "const": [""],
        "default": [],
        "type": lambda s: s.split(","),
        "help": "prevent specified spec hooks from being run",
    },
    {
        "name": ["--build-log-hook-blacklist"],
        "choices": plugin_manager.build_log_hooks.get_all_plugins(),
        "available_choices": plugin_manager.build_log_hooks.get_supported_plugins(),
        "nargs": "?",
        "const": [""],
        "default": [],
        "type": lambda s: s.split(","),
        "help": "prevent specified build log hooks from being run"
    },
    # behavior control
    {
        "name": ["--bugzilla-id"],
        "metavar": "BUG_ID",
        "default": None,
        "help": "do a rebase based on Upstream Release Monitoring bugzilla"
    },
    {
        "name": ["--non-interactive"],
        "default": False,
        "switch": True,
        "dest": "non_interactive",
        "help": "do not interact with user",
    },
    {
        "name": ["--favor-on-conflict"],
        "choices": ["downstream", "upstream", "off"],
        "default": "off",
        "dest": "favor_on_conflict",
        "help": "favor downstream or upstream changes when conflicts appear",
    },
    {
        "name": ["--not-download-sources"],
        "default": False,
        "switch": True,
        "help": "do not download sources",
    },
    {
        "name": ["-w", "--keep-workspace"],
        "default": False,
        "switch": True,
        "help": "do not remove workspace directory after finishing",
    },
    {
        "name": ["--apply-changes"],
        "default": False,
        "switch": True,
        "help": "apply {} after a successful rebase".format(CHANGES_PATCH),
    },
    {
        "name": ["--disable-inapplicable-patches"],
        "default": False,
        "switch": True,
        "dest": "disable_inapplicable_patches",
        "help": "disable inapplicable patches in rebased SPEC file",
    },
    {
        "name": ["--skip-version-check"],
        "default": False,
        "switch": True,
        "help": "force rebase even if current version is newer than requested version",
    },
    {
        "name": ["--update-sources"],
        "default": False,
        "switch": True,
        "help": "update \"sources\" file and upload new sources to lookaside cache",
    },
    {
        "name": ["--skip-upload"],
        "default": False,
        "switch": True,
        "help": "skip uploading new sources to lookaside cache",
    },
    {
        "name": ["--force-build-log-hooks"],
        "default": False,
        "switch": True,
        "help": "enforce running of build log hooks (even in non-interactive mode)",
    },
    # remote builder options
    {
        "name": ["--builds-nowait"],
        "default": False,
        "switch": True,
        "help": "do not wait for remote builds to finish",
    },
    {
        "name": ["--build-tasks"],
        "dest": "build_tasks",
        "metavar": "OLD_TASK,NEW_TASK",
        "type": lambda s: s.split(','),
        "help": "comma-separated remote build task ids",
    },
    # additional local builder options
    {
        "name": ["--builder-options"],
        "default": None,
        "metavar": "BUILDER_OPTIONS",
        "help": "enable arbitrary local builder option(s), enclose %(metavar)s in quotes "
                "to pass more than one",
    },
    {
        "name": ["--srpm-builder-options"],
        "default": None,
        "metavar": "SRPM_BUILDER_OPTIONS",
        "help": "enable arbitrary local srpm builder option(s), enclose %(metavar)s in quotes "
                "to pass more than one",
    },
    # misc
    {
        "name": ["--lookaside-cache-preset"],
        "choices": ["fedpkg", "centpkg", "rhpkg", "rhpkg-sha512"],
        "default": "fedpkg",
        "help": "use specified lookaside cache configuration preset, defaults to %(default)s",
    },
    {
        "name": ["--changelog-entry"],
        "default": "- New upstream release %{version}",
        "help": "text to use as changelog entry, can contain RPM macros, which will be expanded",
    },
    {
        "name": ["--no-changelog-entry"],
        "default": False,
        "switch": True,
        "help": "do not add a changelog entry at all",
    },
    {
        "name": ["--config-file"],
        "default": os.path.join(CONFIG_PATH, CONFIG_FILENAME),
        "help": "path to a configuration file, defaults to %(default)s",
    },
    {
        "name": ["-D", "--define"],
        "default": [],
        "append": True,
        "dest": "rpmmacros",
        "metavar": "'MACRO EXPR'",
        "help": "define an rpm macro, can be used multiple times",
    },
    # sources
    {
        "name": ["sources"],
        "metavar": "SOURCES",
        "nargs": "?",
        "default": None,
        "help": "version number or filename of the new source archive",
    },
]

def traverse_options(options):
    group_index = 0
    for opt in options:
        if isinstance(opt, list):
            for inner_opt in opt:
                yield dict(group=group_index, **inner_opt)
            group_index += 1
        else:
            yield opt


squaredWeight = None
def performCollection(cityLevel, filename):
    import os
    if cityLevel:
        outputDir = 'GoogleTrendsCity/'
        if not os.path.exists(outputDir):
            os.mkdir(outputDir)
    else:
        outputDir = 'GoogleTrendsCountry/'
        if not os.path.exists(outputDir):
            os.mkdir(outputDir)

    import pickle
    infile = open(filename,'rb')
    kw_list = pickle.load(infile)
    infile.close()

    import time
    from pytrends.request import TrendReq
    pytrends = TrendReq()
    count = 0
    for keyword in kw_list:
        count += 1
        if not '/' in keyword:
            filename = outputDir+ keyword + '.pickle'
            from os import path
            if not path.exists(filename):
                pytrends.build_payload([keyword])

                if cityLevel:
                    df = pytrends.interest_by_region(resolution='CITY', inc_low_vol=True, inc_geo_code=False)
                else:
                    df = pytrends.interest_by_region(resolution='COUNTRY', inc_low_vol=True, inc_geo_code=False)

                import pickle
                outfile = open(filename,'wb')
                pickle.dump(df,outfile)
                outfile.close()
                #time.sleep(3)
        print(count)

def formCityList(filename):
    filenameToWriteTo = "allCities.pickle"
    from os import path
    if not path.exists(filenameToWriteTo):
        outputDir = 'GoogleTrendsCity/'

        import pickle
        infile = open(filename,'rb')
        kw_list = pickle.load(infile)
        infile.close()

        count = 0
        allCities = {}
        for keyword in kw_list:
            print(count)
            if count != 897:
                filename = outputDir+ keyword + '.pickle'
                from os import path
                if path.exists(filename):
                    import pickle
                    infile = open(filename,'rb')
                    df = pickle.load(infile)
                    infile.close()

                    if len(df) != 0:
                        cities = list(df['geoName'])
                        latLong = list(df['coordinates'])
                        for i in range(0, len(cities), 1):
                            cityName = cities[i]
                            if not cityName.lower() in allCities:
                                allCities[cityName.lower()] = latLong[i]
            count += 1

        import pickle
        outfile = open(filenameToWriteTo,'wb')
        pickle.dump(allCities, outfile)
        outfile.close()

def averageAndStdDevAcrossAssociationsMadeByGoogle(cityLevel, filename):
    import os
    if cityLevel:
        outputDir = 'GoogleTrendsCity/'
        if not os.path.exists(outputDir):
            os.mkdir(outputDir)
    else:
        outputDir = 'GoogleTrendsCountry/'
        if not os.path.exists(outputDir):
            os.mkdir(outputDir)

    import os
    import pickle
    infile = open(filename,'rb')
    kw_list = pickle.load(infile)
    infile.close()
    print(len(kw_list))
    count = 0
    valuesReturned = []
    zeroValueCount = 0
    for keyword in kw_list:
        if keyword != 'con':
            filename = outputDir+ keyword + '.pickle'
            from os import path
            if path.exists(filename):
                import pickle
                infile = open(filename,'rb')
                df = pickle.load(infile)
                infile.close()
                try:
                    valuesReturned.append(len(df))
                except:
                    zeroValueCount += 1
                count += 1

    import numpy as np
    print(np.average(valuesReturned))
    print(np.std(valuesReturned))
    print(zeroValueCount)
    print(count)

def assignRegion(cityLevel, filename, outputFile):
    import os
    outputDirAssignRegion = 'AssignRegion/'
    if not os.path.exists(outputDirAssignRegion):
        os.mkdir(outputDirAssignRegion)
    outputDirAssignRegion = 'AssignRegionWeightSquared/'
    if not os.path.exists(outputDirAssignRegion):
        os.mkdir(outputDirAssignRegion)
    outputDirAssignRegion = 'AssignRegion/'
    if squaredWeight:
        outputDirAssignRegion = 'AssignRegionWeightSquared/'

    isoToLat, isoToLong = getCountryInfo()
    print(isoToLat)
    print(isoToLong)
    import os
    if cityLevel:
        outputDir = 'GoogleTrendsCity/'
        if not os.path.exists(outputDir):
            os.mkdir(outputDir)
    else:
        outputDir = 'GoogleTrendsCountry/'
        if not os.path.exists(outputDir):
            os.mkdir(outputDir)

    import pickle
    infile = open(filename,'rb')
    kw_list = pickle.load(infile)
    infile.close()

    noData = 0
    noWeightsOver0 = 0
    rows = [['keyword', 'using top 1', 'using top 3', 'using weight > 50', 'all']]
    keywordToRegion1 = {}
    keywordToRegion2 = {}
    keywordToRegion3 = {}
    keywordToRegion4 = {}
    for keyword in kw_list:
        if keyword != 'con':
            filename = outputDir+ keyword + '.pickle'
            from os import path
            if path.exists(filename):
                import pickle
                infile = open(filename,'rb')
                df = pickle.load(infile)
                infile.close()

                dataReturnedByTrends = False
                try:
                    weights = list(df['value'])
                    weightsValues = []
                    for value in weights:
                        weightsValues.append(value[0])
                    df['weights'] = weightsValues
                    df = df.loc[df['weights'] > 0]
                    dataReturnedByTrends = True
                except:
                    noData += 1

                if dataReturnedByTrends:
                    if len(df) > 0:
                        df1 = df.nlargest(1, 'weights')
                        df2 = df.nlargest(3, 'weights')
                        df3 = df.loc[df['weights'] > 50]
                        df4 = df

                        label1 = predictRegion(cityLevel, df1, isoToLong)
                        if label1 != None:
                            keywordToRegion1[keyword] = label1
                        label2 = predictRegion(cityLevel, df2, isoToLong)
                        if label2 != None:
                            keywordToRegion2[keyword] = label2
                        label3 = predictRegion(cityLevel, df3, isoToLong)
                        if label3 != None:
                            keywordToRegion3[keyword] = label3
                        label4 = predictRegion(cityLevel, df4, isoToLong)
                        if label4 != None:
                            keywordToRegion4[keyword] = label4
                        if label1 != None or label2 != None or label3 != None or label4 != None:
                            rows.append([keyword, label1, label2, label3, label4])
                    else:
                        noWeightsOver0 += 1
    print(str(noData) + " out of " + str(len(kw_list)) + " tokens had no data.")
    print(str(noWeightsOver0) + " out of " + str(len(kw_list)) + " tokens had no weights.")

    writeRowsToCSV(rows, outputDirAssignRegion+outputFile)

    rows = [['Resriction', 'Predictions', 'NA_SA', 'AF_EUR', 'AS_OC', 'Total Accuracy', 'Total Predictions']]
    rows.append(['using top 1']+evaluatePredictions(keywordToRegion1))
    rows.append(['using top 3']+evaluatePredictions(keywordToRegion2))
    rows.append(['using weight > 50']+evaluatePredictions(keywordToRegion3))
    rows.append(['all']+evaluatePredictions(keywordToRegion4))

    writeRowsToCSV(rows, outputDirAssignRegion+"Performance"+outputFile)

def predictRegion(cityLevel, df, isoToLong):
    import numpy as np
    if cityLevel:
        geoNameToCoordinates = dict(zip(list(df["geoName"]), list(df['coordinates'])))
        geoNameToWeight = dict(zip(list(df["geoName"]), list(df['weights'])))

        label = None
        l1 = 0
        l2 = 0
        l3 = 0
        for geoName in geoNameToCoordinates:
            coordinate = geoNameToCoordinates[geoName]
            weight = geoNameToWeight[geoName]
            if squaredWeight:
                weight = weight*weight
            long = coordinate['lng']
            if long <= -25:
                l1 += weight
            elif long <= 65:
                l2 += weight
            else:
                l3 += weight
        Americas = l1
        Africa_Europe = l2
        Asia_Australia = l3
        total = l1+l2+l3
        if total > 0:
            ratioAmericas = float(Americas)/float(total)
            ratioAfrica_Europe = float(Africa_Europe)/float(total)
            ratioAsia_Australia = float(Asia_Australia)/float(total)
            ratioMax = np.max([ratioAmericas, ratioAfrica_Europe, ratioAsia_Australia])
            label = None
            if ratioAmericas == ratioMax:
                label = "Americas"
            elif ratioAfrica_Europe == ratioMax:
                label = "Africa_Europe"
            else:
                label = "Asia_Australia"
        else:
            label = None
    else:
        countryISOCodeToWeight = dict(zip(list(df["geoCode"]), list(df['weights'])))

        label = None
        l1 = 0
        l2 = 0
        l3 = 0
        for countryISOCode in countryISOCodeToWeight:
            weight = countryISOCodeToWeight[countryISOCode]
            long = isoToLong[countryISOCode]
            if long <= -25:
                l1 += weight
            elif long <= 65:
                l2 += weight
            else:
                l3 += weight
        Americas = l1
        Africa_Europe = l2
        Asia_Australia = l3
        total = l1+l2+l3
        if total > 0:
            ratioAmericas = float(Americas)/float(total)
            ratioAfrica_Europe = float(Africa_Europe)/float(total)
            ratioAsia_Australia = float(Asia_Australia)/float(total)
            ratioMax = np.max([ratioAmericas, ratioAfrica_Europe, ratioAsia_Australia])
            label = None
            if ratioAmericas == ratioMax:
                label = "Americas"
            elif ratioAfrica_Europe == ratioMax:
                label = "Africa_Europe"
            else:
                label = "Asia_Australia"
        else:
            label = None
    return label

def getCountryInfo():
    #file with average lat, long for each country
    #country info from: https://gist.github.com/tadast/8827699#file-countries_codes_and_coordinates-csv
    import pandas as pd
    filePath = 'countries_codes_and_coordinates.csv'
    df=pd.read_csv(filePath, encoding='utf-8')
    print(df.columns)
    temp = list(df["Alpha-2 code"])
    countryList = []
    for isoCode in temp:
        countryList.append(str(isoCode).strip().replace('"', ''))
    latitudeList = []
    temp = list(df['Latitude (average)'])
    for s in temp:
        latitudeList.append(float(s.strip().replace('"', '')))
    longitudeList = []
    temp = list(df['Longitude (average)'])
    for s in temp:
        longitudeList.append(float(s.strip().replace('"', '')))

    isoToLat = dict(zip(countryList, latitudeList))
    isoToLong = dict(zip(countryList, longitudeList))

    isoToLat['CW'] = 12.1696
    isoToLong['CW'] = -68.9900
    isoToLat['XK'] = 42.6026
    isoToLong['XK'] = 20.9030
    isoToLat['SX'] = 18.0425
    isoToLong['SX'] = -63.0548
    isoToLat['MF'] = 18.0826
    isoToLong['MF'] = -63.0523
    isoToLat['AX'] = 60.1785
    isoToLong['AX'] = 19.9156
    isoToLat['BL'] = 17.9000
    isoToLong['BL'] = -62.8333
    isoToLat['BQ'] = 12.1684
    isoToLong['BQ'] = -68.3082

    return isoToLat, isoToLong

def writeRowsToCSV(rows, fileToWriteToCSV):
    import csv
    if len(rows) > 0:
        with open(fileToWriteToCSV, "w", encoding='utf-8') as fp:
            a = csv.writer(fp, delimiter=',')
            a.writerows(rows)
            fp.close()
            print("Written " + str(len(rows)) + " rows to: " + fileToWriteToCSV)

def evaluatePredictions(tokenToPrediction):
    import pandas as pd
    filePath = "Input/combineDBsCoordinateGroundTruthDiv3.csv"
    df=pd.read_csv(filePath, encoding='utf-8')

    tokenToLabel = dict(zip(list(df["id"]), list(df['label'])))

    l1 = 0
    l2 = 0
    l3 = 0
    for token in tokenToPrediction:
        prediction = tokenToPrediction[token]
        if prediction == 'Americas':
            l1 += 1
        elif prediction == 'Africa_Europe':
            l2 += 1
        else:
            l3 += 1

    print(str(l1) + ", " + str(l2) + ", " + str(l3) + " Americas vs. Africa_Europe vs. Asia_Australia")

    correct = {'Americas':0,'Africa_Europe':0,'Asia_Australia':0}
    wrong = {'Americas':0,'Africa_Europe':0,'Asia_Australia':0}
    for token in tokenToPrediction:
        label = tokenToLabel[token]
        prediction = tokenToPrediction[token]
        if label == prediction:
            if label == 'Americas':
                correct['Americas'] += 1
            elif label == 'Africa_Europe':
                correct['Africa_Europe'] += 1
            elif label == 'Asia_Australia':
                correct['Asia_Australia'] += 1
            else:
                print("unknown label")
                import sys
                sys.exit()
        else:
            if label == 'Americas':
                wrong['Americas'] += 1
            elif label == 'Africa_Europe':
                wrong['Africa_Europe'] += 1
            elif label == 'Asia_Australia':
                wrong['Asia_Australia'] += 1
            else:
                print("unknown label")
                import sys
                sys.exit()

    import numpy as np
    accuracy = float(np.sum(list(correct.values())))/float(np.sum(list(correct.values()))+np.sum(list(wrong.values())))
    row = []
    predictions = []
    for key in ['Americas', 'Africa_Europe', 'Asia_Australia']:
        predictions.append(float(correct[key]+wrong[key]))
    precision = []
    for key in ['Americas', 'Africa_Europe', 'Asia_Australia']:
        precision.append(round(float(correct[key])/float(correct[key]+wrong[key])*100,2))

    row = [str(predictions)]+precision
    row += [round(accuracy*100, 2), float(np.sum(list(correct.values()))+np.sum(list(wrong.values())))]

    return row

def compareQueryCityLocationVsTopTrendingCityLocation():
    rows = [['query city', 'query city geo', 'top Google Trends city', 'top city geo', 'distance between two']]
    distanceBetweenGoogleQueryCityAndTopCityFromGoogleTrends = []
    noWeightsOver0 = 0
    noData = 0
    filename = "allCities.pickle"
    import pickle
    infile = open(filename,'rb')
    cityToLatLong = pickle.load(infile)
    infile.close()

    count = 0
    for cityName in cityToLatLong:
        if not '/' in cityName:
            queryCityCoordinates = (cityToLatLong[cityName]['lat'], cityToLatLong[cityName]['lng'])
            queryCityName = cityName
            outputDir = 'GoogleTrendsCity/'
            filename = outputDir+ cityName + '.pickle'
            from os import path
            if path.exists(filename):
                count += 1
                import pickle
                infile = open(filename,'rb')
                df = pickle.load(infile)
                infile.close()

                try:
                    weights = list(df['value'])
                    weightsValues = []
                    for value in weights:
                        weightsValues.append(value[0])
                    df['weights'] = weightsValues
                    df = df.loc[df['weights'] > 0]

                    if len(df) > 0:
                        df1 = df.nlargest(1, 'weights')

                        topGoogleTrendCityCoordinates = list(df1['coordinates'])[0]
                        topGoogleTrendCityName = list(df1['geoName'])[0]
                        topGoogleTrendCityCoordinates = (topGoogleTrendCityCoordinates['lat'], topGoogleTrendCityCoordinates['lng'])

                        from geopy.distance import geodesic
                        from geopy.distance import great_circle

                        distanceBetweenTheTwo = geodesic(queryCityCoordinates, topGoogleTrendCityCoordinates).miles
                        distanceBetweenGoogleQueryCityAndTopCityFromGoogleTrends.append(distanceBetweenTheTwo)
                        rows.append([queryCityName, str(queryCityCoordinates), topGoogleTrendCityName, str(topGoogleTrendCityCoordinates), distanceBetweenTheTwo])
                    else:
                        noWeightsOver0 += 1
                except:
                    noData += 1
    print(str(noData) + " out of " + str(count) + " tokens had no data.")
    print(str(noWeightsOver0) + " out of " + str(count) + " tokens had no weights.")

    import numpy as np
    print(np.average(distanceBetweenGoogleQueryCityAndTopCityFromGoogleTrends))
    print(np.std(distanceBetweenGoogleQueryCityAndTopCityFromGoogleTrends))

    writeRowsToCSV(rows, 'topCityAnalysis.csv')

if __name__ == '__main__':
    pass

    step1 = False
    if step1:
        performCollection(True, 'Input/459.pickle') #Google Trends at city level
        performCollection(True, 'Input/3183.pickle') #Google Trends at city level
        performCollection(False, 'Input/459.pickle') #Google Trends at country level
        performCollection(False, 'Input/3183.pickle') #Google Trends at country level

    '''Google Trends does not always return the same number of cities
    the following code examines average/standard deviation for the number of cities returned'''
    if False:
        averageAndStdDevAcrossAssociationsMadeByGoogle(True, 'Input/459.pickle')
        averageAndStdDevAcrossAssociationsMadeByGoogle(True, 'Input/3183.pickle')
        averageAndStdDevAcrossAssociationsMadeByGoogle(False, 'Input/459.pickle')
        averageAndStdDevAcrossAssociationsMadeByGoogle(False, 'Input/3183.pickle')

    step2 = True
    if step2:
        squaredWeight = True #This parameter raises the weight associated by Google via weight=weight*weight
        filename = 'Input/459.pickle'
        outputFilename = '459.csv'
        assignRegion(True, filename, str(True)+outputFilename)
        assignRegion(False, filename, str(False)+outputFilename)
        filename = 'Input/3183.pickle'
        outputFilename = '3183.csv'
        assignRegion(True, filename, str(True)+outputFilename)
        assignRegion(False, filename, str(False)+outputFilename)

    '''Google Trends at city resolution associates tokens with city locations
    For each city, the city name and its coordinates are stored in file "allCities.pickle"
    Next we send each city name to Google Trends and utilize the top city result
    For example 'chicago' is sent and the top city result from Google Trends is returned
    The coordinates for both city query and the Google trend city are known
    These coordinates are used to compute distance in miles.
    Over 4789 cities on average the top city result from Google Trends is 362 miles away +/- 1335 miles.
    So Google Trends not same as geocoding, but for query such as Moscow Google is able to capture that
    this query is not likely to be utilized by Russian speakers in Moscow since those would like utilize
    Cyrilis version.
    The results of comparison for each city stored in: topCityAnalysis.csv'''
    step3 = False
    if step3:
        formCityList('Input/3183.pickle') #forms list of cities from Google Trend Associations, stores into "allCities.pickle"
        performCollection(True, "allCities.pickle") #Google Trends at city level
        compareQueryCityLocationVsTopTrendingCityLocation()