import os
from unittest.mock import patch

import numpy as np
import pytest
import rasterio
import requests
from constants import KERNEL_SIZE
from shapely.geometry import Polygon
from write_to_db import (  # to_datetime_str,
    calc_peak_power,
    create_mask,
    create_polygon_and_area,
    extract_polygon_coordinates,
    extract_shapes,
    prediction_to_polygons,
    transform_polygon,
    write_to_db,
)

def test_create_mask_with_all_values_below_threshold():
    # Test with a prediction where all values are below the threshold
    prediction = np.random.rand(1, KERNEL_SIZE, KERNEL_SIZE) * 0.4
    expected = np.zeros((KERNEL_SIZE, KERNEL_SIZE), dtype=np.uint8)
    result = create_mask(prediction)
    assert result.shape == (KERNEL_SIZE, KERNEL_SIZE)
    np.testing.assert_array_equal(result, expected)

def test_create_mask_with_all_values_above_threshold():
    # Test with a prediction where all values are above the threshold
    prediction = np.random.rand(1, KERNEL_SIZE, KERNEL_SIZE) * 0.6 + 0.5
    expected = np.ones((KERNEL_SIZE, KERNEL_SIZE), dtype=np.uint8)
    result = create_mask(prediction)
    assert result.shape == (KERNEL_SIZE, KERNEL_SIZE)
    np.testing.assert_array_equal(result, expected)

def test_create_mask_with_some_values_above_threshold():
    # Test with a prediction where some values are above and below the threshold
    prediction = np.zeros((1, KERNEL_SIZE, KERNEL_SIZE))
    prediction[
        0, : KERNEL_SIZE // 2, : KERNEL_SIZE // 2
    ] = 0.4  # values below the threshold
    prediction[
        0, KERNEL_SIZE // 2 :, KERNEL_SIZE // 2 :
    ] = 0.6  # values above the threshold
    expected = np.zeros((KERNEL_SIZE, KERNEL_SIZE), dtype=np.uint8)
    expected[KERNEL_SIZE // 2 :, KERNEL_SIZE // 2 :] = 1
    result = create_mask(prediction)
    assert result.shape == (KERNEL_SIZE, KERNEL_SIZE)
    np.testing.assert_array_equal(result, expected)

def test_create_mask_with_invalid_input():
    # Test with invalid input
    prediction = "not a numpy array"
    with pytest.raises(TypeError):
        create_mask(prediction)

def test_create_mask_with_invalid_shape():
    # Test with invalid shape
    prediction = np.random.rand(2, KERNEL_SIZE, KERNEL_SIZE)
    with pytest.raises(ValueError):
        create_mask(prediction)

def test_create_mask_with_invalid_dtype():
    # Test with invalid dtype
    prediction = np.random.randint(0, 2, (1, KERNEL_SIZE, KERNEL_SIZE))
    with pytest.raises(TypeError):
        create_mask(prediction)

def test_extract_shapes_with_invalid_dtype():
    # Test with invalid dtype
    mask = np.random.rand(5, 5)
    transform = rasterio.transform.from_origin(0, 5, 1, 1)
    with pytest.raises(TypeError):
        extract_shapes(mask, transform)

def test_extract_shapes_with_invalid_dimensions():
    # Test with invalid dimensions
    mask = np.random.randint(0, 2, (1, 5, 5), dtype=np.uint8)
    transform = rasterio.transform.from_origin(0, 5, 1, 1)
    with pytest.raises(ValueError):
        extract_shapes(mask, transform)

def test_extract_shapes_with_single_shape():
    # Test with a mask that contains a single shape
    mask = np.zeros((5, 5), dtype=np.uint8)
    mask[1:4, 1:4] = 1
    transform = rasterio.transform.from_origin(0, 5, 1, 1)
    expected = [
        (
            {
                "type": "Polygon",
                "coordinates": [
                    [(1.0, 4.0), (1.0, 1.0), (4.0, 1.0), (4.0, 4.0), (1.0, 4.0)]
                ],
            },
            1.0,
        )
    ]
    result = extract_shapes(mask, transform)
    assert len(result) == 1
    assert result == expected

def test_extract_shapes_with_multiple_shapes():
    # Test with a mask that contains multiple shapes
    mask = np.zeros((5, 5), dtype=np.uint8)
    mask[1:3, 1:3] = 1
    mask[3:5, 3:5] = 1
    transform = rasterio.transform.from_origin(0, 5, 1, 1)
    expected = [
        (
            {
                "type": "Polygon",
                "coordinates": [
                    [(1.0, 4.0), (1.0, 2.0), (3.0, 2.0), (3.0, 4.0), (1.0, 4.0)]
                ],
            },
            1.0,
        ),
        (
            {
                "type": "Polygon",
                "coordinates": [
                    [(3.0, 2.0), (3.0, 0.0), (5.0, 0.0), (5.0, 2.0), (3.0, 2.0)]
                ],
            },
            1.0,
        ),
    ]
    result = extract_shapes(mask, transform)
    assert len(result) == 2
    assert result == expected
    assert isinstance(result, list)

def test_extract_shapes_with_no_shapes():
    # Test with a mask that contains no shapes
    mask = np.zeros((5, 5), dtype=np.uint8)
    transform = rasterio.transform.from_origin(0, 5, 1, 1)
    result = extract_shapes(mask, transform)
    assert len(result) == 0

def test_create_polygon_and_area_with_valid_input():
    # Test with valid input
    shape = list(
        (
            {
                "type": "Polygon",
                "coordinates": [
                    [(1.0, 4.0), (1.0, 1.0), (4.0, 1.0), (4.0, 4.0), (1.0, 4.0)]
                ],
            },
            1.0,
        )
    )
    expected_polygon = Polygon(
        [(1.0, 4.0), (1.0, 1.0), (4.0, 1.0), (4.0, 4.0), (1.0, 4.0)]
    )
    expected_area = expected_polygon.area
    result_polygon, result_area = create_polygon_and_area(shape)
    assert result_polygon.equals(expected_polygon)
    assert result_area == expected_area

def test_create_polygon_and_area_with_invalid_input():
    # Test with invalid input
    shape = "not a valid shape"
    with pytest.raises(TypeError):
        create_polygon_and_area(shape)

def test_create_polygon_and_area_with_invalid_coordinates():
    # Test with invalid coordinates
    shape = list(
        (
            {
                "type": "Polygon",
                "coordinates": "not a valid coordinates",
            },
            1.0,
        )
    )
    with pytest.raises(ValueError):
        create_polygon_and_area(shape)

def test_transform_polygon_with_valid_input():
    # Test with valid input
    polygon = Polygon([(1.0, 4.0), (1.0, 1.0), (4.0, 1.0), (4.0, 4.0), (1.0, 4.0)])
    crs = "EPSG:3857"
    result = transform_polygon(polygon, crs)
    assert isinstance(result, Polygon)
    assert result.is_valid

def test_transform_polygon_with_invalid_polygon():
    # Test with invalid polygon
    polygon = "not a valid polygon"
    crs = "EPSG:3857"
    with pytest.raises(TypeError):
        transform_polygon(polygon, crs)

def test_transform_polygon_with_invalid_crs():
    # Test with invalid CRS
    polygon = Polygon([(1.0, 4.0), (1.0, 1.0), (4.0, 1.0), (4.0, 4.0), (1.0, 4.0)])
    crs = 1234
    with pytest.raises(TypeError):
        transform_polygon(polygon, crs)

def test_transform_polygon_with_nonexistent_crs():
    # Test with non-existent CRS
    polygon = Polygon([(1.0, 4.0), (1.0, 1.0), (4.0, 1.0), (4.0, 4.0), (1.0, 4.0)])
    crs = "EPSG:9999"
    with pytest.raises(ValueError):
        transform_polygon(polygon, crs)

def test_prediction_to_polygons_with_valid_input():
    # Test with valid input
    prediction = np.random.rand(1, KERNEL_SIZE, KERNEL_SIZE)
    metadata = {
        "transform": rasterio.transform.from_origin(0, 5, 1, 1),
        "crs": "EPSG:3857",
    }
    polygons, areas = prediction_to_polygons(prediction, metadata)
    assert isinstance(polygons, list)
    assert isinstance(areas, list)
    assert len(polygons) == len(areas)

def test_prediction_to_polygons_with_invalid_prediction():
    # Test with invalid prediction
    prediction = "not a numpy array"
    metadata = {
        "transform": rasterio.transform.from_origin(0, 5, 1, 1),
        "crs": "EPSG:3857",
    }
    with pytest.raises(TypeError):
        prediction_to_polygons(prediction, metadata)

def test_prediction_to_polygons_with_invalid_metadata():
    # Test with invalid metadata
    prediction = np.random.rand(1, KERNEL_SIZE, KERNEL_SIZE)
    metadata = "not a dictionary"
    with pytest.raises(TypeError):
        prediction_to_polygons(prediction, metadata)

def test_prediction_to_polygons_with_missing_transform_in_metadata():
    # Test with missing 'transform' key in metadata
    prediction = np.random.rand(1, KERNEL_SIZE, KERNEL_SIZE)
    metadata = {"crs": "EPSG:3857"}
    with pytest.raises(KeyError):
        prediction_to_polygons(prediction, metadata)

def test_prediction_to_polygons_with_missing_crs_in_metadata():
    # Test with missing 'crs' key in metadata
    prediction = np.random.rand(1, KERNEL_SIZE, KERNEL_SIZE)
    metadata = {"transform": rasterio.transform.from_origin(0, 5, 1, 1)}
    with pytest.raises(KeyError):
        prediction_to_polygons(prediction, metadata)

# def test_to_datetime_str_with_valid_input():
#     # Test with valid input
#     date_string = "2022-01-01"
#     expected = "2022-01-01"
#     result = to_datetime_str(date_string)
#     assert result == expected

# def test_to_datetime_str_with_invalid_format():
#     # Test with date string in invalid format
#     date_string = "01-01-2022"
#     with pytest.raises(ValueError):
#         to_datetime_str(date_string)

# def test_to_datetime_str_with_invalid_type():
#     # Test with non-string input
#     date_string = 20220101
#     with pytest.raises(TypeError):
#         to_datetime_str(date_string)

# def test_to_datetime_str_with_empty_string():
#     # Test with empty string
#     date_string = ""
#     with pytest.raises(ValueError):
#         to_datetime_str(date_string)

def test_extract_polygon_coordinates_with_valid_input():
    # Test with valid input
    polygon = Polygon([(0, 0), (1, 1), (1, 0)])
    expected_latitudes = [0, 1, 0, 0]
    expected_longitudes = [0, 1, 1, 0]
    result_latitudes, result_longitudes = extract_polygon_coordinates(polygon)
    assert result_latitudes == expected_latitudes
    assert result_longitudes == expected_longitudes

def test_extract_polygon_coordinates_with_invalid_type():
    # Test with non-Polygon input
    polygon = "not a polygon"
    with pytest.raises(TypeError):
        extract_polygon_coordinates(polygon)

def test_extract_polygon_coordinates_with_no_exterior_ring():
    # Test with Polygon that has no exterior ring
    polygon = Polygon()
    with pytest.raises(ValueError):
        extract_polygon_coordinates(polygon)

def test_calc_peak_power_with_valid_input():
    # Test with valid input
    area_in_sq_m = 10000
    expected = 1.0
    result = calc_peak_power(area_in_sq_m)
    assert result == expected

def test_calc_peak_power_with_zero():
    # Test with zero area
    area_in_sq_m = 0
    expected = 0.0
    result = calc_peak_power(area_in_sq_m)
    assert result == expected

def test_calc_peak_power_with_negative_input():
    # Test with negative area
    area_in_sq_m = -1
    with pytest.raises(ValueError):
        calc_peak_power(area_in_sq_m)

def test_calc_peak_power_with_non_number_input():
    # Test with non-number input
    area_in_sq_m = "not a number"
    with pytest.raises(TypeError):
        calc_peak_power(area_in_sq_m)

@patch("requests.post")
@patch.dict(os.environ, {"JWT": "test_token"}, clear=True)
def test_write_to_db_with_valid_input(mock_post):
    # Test with valid input
    mock_post.return_value.raise_for_status.return_value = None
    polygon = Polygon([(0, 0), (1, 1), (1, 0)])
    area = 0.5
    tile_date = "2022-01-01"
    filename = "test.tif"
    assert write_to_db(polygon, area, tile_date, filename)

@patch("requests.post")
@patch.dict(os.environ, {"JWT": "test_token"}, clear=True)
def test_write_to_db_with_invalid_polygon(mock_post):
    # Test with invalid polygon
    polygon = "not a polygon"
    area = 0.5
    tile_date = "2022-01-01"
    filename = "test.tif"
    with pytest.raises(TypeError):
        write_to_db(polygon, area, tile_date, filename)

@patch("requests.post")
@patch.dict(os.environ, {"JWT": "test_token"}, clear=True)
def test_write_to_db_with_invalid_area(mock_post):
    # Test with invalid area
    polygon = Polygon([(0, 0), (1, 1), (1, 0)])
    area = -1
    tile_date = "2022-01-01"
    filename = "test.tif"
    with pytest.raises(ValueError):
        write_to_db(polygon, area, tile_date, filename)

@patch("requests.post")
@patch.dict(os.environ, {"JWT": "test_token"}, clear=True)
def test_write_to_db_with_invalid_tile_date(mock_post):
    # Test with invalid tile_date
    polygon = Polygon([(0, 0), (1, 1), (1, 0)])
    area = 0.5
    tile_date = 20220101
    filename = "test.tif"
    with pytest.raises(TypeError):
        write_to_db(polygon, area, tile_date, filename)

@patch("requests.post")
@patch.dict(os.environ, {"JWT": "test_token"}, clear=True)
def test_write_to_db_with_invalid_filename(mock_post):
    # Test with invalid filename
    polygon = Polygon([(0, 0), (1, 1), (1, 0)])
    area = 0.5
    tile_date = "2022-01-01"
    filename = 123
    with pytest.raises(TypeError):
        write_to_db(polygon, area, tile_date, filename)

@patch("requests.post")
@patch.dict(os.environ, {"JWT": "test_token"}, clear=True)
def test_write_to_db_with_request_exception(mock_post):
    # Test with a request exception
    mock_post.return_value.raise_for_status.side_effect = (
        requests.exceptions.RequestException
    )
    polygon = Polygon([(0, 0), (1, 1), (1, 0)])
    area = 0.5
    tile_date = "2022-01-01"
    filename = "test.tif"
    with pytest.raises(requests.exceptions.RequestException):
        write_to_db(polygon, area, tile_date, filename)


#  type: ignore
import wrapt

@wrapt.when_imported("pydantic.schema")
def hook_pydantic_schema(schema):
    # This method is called by fastapi to get the schema of a model.
    # We need to replace it with a wrapper that will return the schema
    # of the assembled class instead of the base class to make the extendable
    # models work with fastapi.
    def _model_process_schema_wrapper(wrapped, instance, args, kwargs):
        model = args[0]
        if hasattr(model, "_get_assembled_cls"):
            model = model._get_assembled_cls()
            args = (model,) + args[1:]
            from pydantic import schema as schema_module

            # we must also extend the list ok know model name to the
            # assembled classes since fastapi by default restrict this list
            # to the classes defined in the service definition
            flat_models = schema_module.get_flat_models_from_models([model])
            model_name_map = schema_module.get_model_name_map(flat_models)
            kwargs["model_name_map"] = model_name_map
        return wrapped(*args, **kwargs)

    wrapt.wrap_function_wrapper(
        schema, "model_process_schema", _model_process_schema_wrapper
    )

@wrapt.when_imported("fastapi.routing")
def hook_fastapi_routing(routing):
    # This method is called by fastapi to serialize the response of a route.
    # We need to replace it with a wrapper that will use the extended class
    # instead of the base class to serialize the response.
    def _serialize_response_wrapper(wrapped, instance, args, kwargs):
        field = kwargs["field"]
        if hasattr(field, "_get_assembled_cls"):
            field = field._get_assembled_cls()
            kwargs["field"] = field
        return wrapped(**kwargs)

    wrapt.wrap_function_wrapper(
        routing, "serialize_response", _serialize_response_wrapper
    )

@wrapt.when_imported("fastapi.utils")
def hook_fastapi_utils(utils):
    # This method is called by fastapi to create a new field from an existing
    # field when the field is used as a response model. The motivation is to
    # ensure that the response model is not modified by the route handler.
    # We need to replace it with a wrapper that will use the extended class
    # as new field instead of a clone of the base class. Semantically, we
    # still ensure that the response model is not modified by the route handler.
    # since we enforce the use of the expected response model.
    def _create_cloned_field_wrapper(wrapped, instance, args, kwargs):
        field = args[0]
        if hasattr(field.type_, "_get_assembled_cls"):
            return field
        return wrapped(*args, **kwargs)

    wrapt.wrap_function_wrapper(
        utils, "create_cloned_field", _create_cloned_field_wrapper
    )


                                     # Chapter 2 : Strings
# ======================= Problem 1 ==================
Word = input("Enter a word to convert")

def pig_latin(Word):
    if Word[0] in "aeiou":
        return("{}way".format(Word))
    else:
        return("{}{}ay".format(Word[1::],Word[0]))
print(pig_latin(Word))

# ======================= Problem 2 ===================
sentence = input("enter a sentence")
list_sentence = sentence.split(" ")            # splitting sentence into words by space delimiter

pig_latin_list = [pig_latin(i) for i in list_sentence]
new_sentence = ' '.join(pig_latin_list)
print(pig_latin_list)
print(new_sentence)

# ======================== Problem 3 ==================
# 2.4 UBBI DUBBI

word = input("enter a word")
list_word = list(word)
print(list_word)

j=0
ubbi =[]
for i in list_word:
    j += 1
    if i in "aeiou":
       ubbi.append("ub{}".format(i))
    else:
        ubbi.append(i)

ubbi_dubbi = "".join(ubbi)
print(ubbi_dubbi)

# ======================== Problem 4 ===================
# sorting a given string

def strsort(x):
    y = ''.join(sorted(x))
    return y
print(strsort('cba'))


import numpy as np
from tsp import get_cost
import random
from collections import deque

def tnm_selection(pop, costs, max_tnm):  # tournament selection
    selection_ix = np.random.randint(len(pop))  # [0, len(pop)-1]
    for _ in np.random.randint(0, len(pop), max_tnm - 1):  # [0, len(pop)-1]
        # check if better (e.g. perform a tournament)
        if costs[_] < costs[selection_ix]:
            selection_ix = _
    return pop[selection_ix]

def rw_selection(pop, costs, _):  # roulette-wheel selection
    costs = [max(costs) - x for x in costs]  # avoid negative scores
    sum_score = sum(costs)
    return pop[np.random.choice(len(pop), p=[x / sum_score for x in costs])]
    # # naive implementation
    # sum_score = sum(scores)
    # pick = random.uniform(0, sum_score)  # [low, high)
    # current = 0
    # for i in range(len(pop)):
    #     current += scores[i]
    #     if current > pick:
    #         return pop[i]

def elt_selection(pop, costs, _):  # elitism
    return pop[np.argmin(costs)]

def crossover(p_1, p_2, r_cross):
    """
    order 1 crossover / OX / order crossover
    :param p_1: parent 1
    :param p_2: parent 2
    :param r_cross: rate of crossover
    """
    if random.random() < r_cross:
        c1, c2 = p_1.copy(), p_2.copy()
        pt_1 = random.randint(0, len(p_1)-1)
        pt_2 = random.randint(0, len(p_2)-1)
        while pt_1 == pt_2:
            pt_2 = random.randint(0, len(p_2)-1)  # let pt_1 != pt_2
        pt_1, pt_2 = (pt_1, pt_2) if pt_1 < pt_2 else(pt_2, pt_1)  # let pt_1 < pt_2
        for _ in range(pt_2 - pt_1):
            c1.remove(p_2[pt_1 + _])
            c2.remove(p_1[pt_1 + _])
        return [c1[:pt_1] + p_2[pt_1:pt_2] + c1[pt_1:], c2[:pt_1] + p_1[pt_1:pt_2] + c2[pt_1:]]
    else:
        return [p_1, p_2]

def mutation(p, r_mut):
    """
    center inverse mutation / CIM
    :param p: parent
    :param r_mut: rate of mutation
    """
    if random.random() < r_mut:
        pt = random.randint(0, len(p)-1)
        return p[:pt][::-1] + p[pt:][::-1]
    else:
        return p

def ga(n, adj_mat, n_pop, r_cross, r_mut, selection_md, max_tnm, term_count):
    pop = [random.sample(range(n), n) for _ in range(n_pop)]
    best_sol, best_cost = pop[0], get_cost(n, adj_mat, pop[0])  # randomly initialize best!
    data = {'cost': deque([]), 'best_cost': deque([])}  # cost means avg_cost
    count = 0
    # select parents
    if selection_md == 'tnm':
        selection_fnc = tnm_selection
    elif selection_md == 'rw':
        selection_fnc = rw_selection
    elif selection_md == 'elt':
        selection_fnc = elt_selection
    else:
        assert 0, 'unknown selection function'
    while True:
        costs = [get_cost(n, adj_mat, _) for _ in pop]
        last_best_cost = best_cost
        for i in range(n_pop):
            if costs[i] < best_cost:
                best_sol, best_cost = pop[i], costs[i]
        if last_best_cost == best_cost:  # best_cost not change
            count += 1
        else:  # best_cost change
            count = 0
        data['cost'].append(np.mean(costs))
        data['best_cost'].append(best_cost)
        if count > term_count:
            return best_sol, best_cost, data
        selected = [selection_fnc(pop, costs, max_tnm=max_tnm) for _ in range(n_pop)]
        # create the next generation
        children = list()
        for i in range(0, n_pop, 2):
            p1, p2 = selected[i], selected[i+1]  # get selected parents in pairs
            for c in crossover(p1, p2, r_cross):  # crossover and mutation
                children.append(mutation(c, r_mut))  # store for next generation
        pop = children  # replace population


import os
import random
import asyncio
from io import BytesIO

import aiohttp
from graia.saya import Saya, Channel
from graia.ariadne.model import Group, Member
from graia.ariadne.message.element import Image
from graia.broadcast.interrupt.waiter import Waiter
from graia.broadcast.interrupt import InterruptControl
from graia.ariadne.event.message import GroupMessage
from graia.ariadne.message.chain import MessageChain
from graia.saya.builtins.broadcast.schema import ListenerSchema
from graia.ariadne.message.parser.twilight import Twilight, RegexMatch, RegexResult

from util.sendMessage import safeSendGroupMessage
from util.control import Permission, Interval
from .WaifuLab import waifu

func = os.path.dirname(__file__).split("\\")[-1]

saya = Saya.current()
channel = Channel.current()
channel.name(func)
bcc = saya.broadcast
inc = InterruptControl(bcc)

http_proxy = {"http://127.0.0.1:33210"}

BROWER = 0

async def get_pic(pic_id: str):
    url = f"https://www.thiswaifudoesnotexist.net/example-{pic_id}.jpg"
    async with aiohttp.ClientSession() as session:
        resp = await session.get(url=url)
        res = await resp.content.read()

    return BytesIO(res).getvalue()

@channel.use(
    ListenerSchema(
        listening_events=[GroupMessage],
        inline_dispatchers=[
            Twilight(
                [
                    RegexMatch("/"),
                    "head" @ RegexMatch("Waifu|头像生成|获取头像|随机头像"),
                ]
            )
        ],
        decorators=[
            Permission.require(),
            Permission.restricter(func),
            Interval.require(),
        ],
    )
)
async def main(group: Group):
    ran = random.randint(1, 100000)
    img = await get_pic(ran)
    await safeSendGroupMessage(
        group, MessageChain.create("你需要的头像已经生成好了哦~", Image(data_bytes=img))
    )

@channel.use(
    ListenerSchema(
        listening_events=[GroupMessage],
        inline_dispatchers=[
            Twilight(
                [
                    RegexMatch("/"),
                    "head" @ RegexMatch("WaifuLab|Lab头像生成|Lab获取头像|Lab头像"),
                    "water" @ RegexMatch("无水印", optional=True),
                ]
            )
        ],
        decorators=[
            Permission.require(),
            Permission.restricter(func),
            Interval.require(),
        ],
    )
)
async def WaifuLab(group: Group, member: Member, water: RegexResult):
    global BROWER
    if BROWER > 5:
        return await safeSendGroupMessage(group, MessageChain.create("当前进程过多，等一会再来哦~"))
    else:
        BROWER += 1
    if water.matched:
        page = waifu(water_mark=False)
    else:
        page = waifu()
    await safeSendGroupMessage(
        group,
        "开始获取头像了哦~\n请耐心等待~",
    )
    stat = await page.gey_browser()
    if not stat:
        BROWER -= 1
        return await safeSendGroupMessage(group, MessageChain.create("连接超时，等一会再来哦~"))
    pic = await page.first_shot()
    pic = pic.getvalue()
    await safeSendGroupMessage(
        group,
        MessageChain.create("连接成功，请选择下一步哦~\n选择[序号]\n继续\n返回\n退出", Image(data_bytes=pic)),
    )
    func_dict = {
        "选择": page.other_shot,
        "继续": page.continues,
        "返回": page.back,
        "退出": page.close,
    }

    @Waiter.create_using_function(
        listening_events=[GroupMessage],
        using_dispatchers=[
            Twilight(
                [
                    "head" @ RegexMatch("选择|继续|返回|退出"),
                    "choose" @ RegexMatch("[0-9]+", optional=True),
                ]
            )
        ],
    )
    async def get_waifu(waiter_member: Member, head: RegexResult, choose: RegexResult):
        tar = 0
        if member.id == waiter_member.id:
            head = head.result.asDisplay()
            if choose.matched:
                choose = int(choose.result.asDisplay())
                if choose > 16:
                    await safeSendGroupMessage(group, "超出选择范围了哦~")
                    return
                if choose == 0:
                    await safeSendGroupMessage(group, "没有0这个选项哦~")
                    return
                tar = choose
            await safeSendGroupMessage(
                group,
                "操作成功~请静待结果~",
            )
            img = func_dict[head]
            if tar != 0:
                img = await img(choose)
            else:
                img = await img()
            if not img:
                return False  # 当返回为空时，选项为退出
            elif isinstance(img, tuple):
                await safeSendGroupMessage(
                    group,
                    MessageChain.create(
                        "获取成功~",
                        Image(data_bytes=img[0].getvalue()),
                        Image(data_bytes=img[1].getvalue()),
                    ),
                )
                return True
            else:
                await safeSendGroupMessage(
                    group,
                    MessageChain.create("头像获取成功~", Image(data_bytes=img.getvalue())),
                )
                return False
        return

    sign = True
    while sign:
        try:
            sign = await asyncio.wait_for(inc.wait(get_waifu), 30)
        except asyncio.TimeoutError:
            await page.close()
            BROWER -= 1
            return await safeSendGroupMessage(group, "等待超时~")
    BROWER -= 1

    await safeSendGroupMessage(group, "你需要的头像已经生成好了哦~")


from os import listdir
from UI.FragmentDictionaryUI import Ui_Form
from Dialogs.InfoDialog import InfoDialog
from Dialogs.InputDialog import InputDialog
from Dialogs.ConfirmationDialog import ConfirmationDialog
from db_helper import Helper
from Elements.DictionaryTitle import DictionaryTitle
from Elements.DictionaryPair import DictionaryPair
from PyQt5 import QtCore, QtWidgets
import threading
import pyttsx3

# Словарь пользователя
class DictFragment(QtWidgets.QWidget, Ui_Form):
    def __init__(self, stacked_widget_top_bar: QtWidgets.QStackedWidget, top_bar_widget1,
                 top_bar_widget2):
        super().__init__()
        self.setupUi(self)
        self.engine = pyttsx3.init()
        voice_id = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\TTS_MS_EN-US_ZIRA_11.0"
        self.engine.setProperty('voice', voice_id)

        # Верхняя панель
        self.stacked_widget_top_bar = stacked_widget_top_bar

        # Элемент верхней панели в окне с названиями тем словарей
        self.top_bar_widget1 = top_bar_widget1
        # Кнопки этой панели
        self.top_bar_widget1.pushButtonAddTheme.clicked.connect(self.button_add_theme_clicked)
        self.top_bar_widget1.pushButtonDeleteTheme.clicked.connect(self.button_delete_theme_clicked)

        # Элемент верхней панели в окне со словами словаря
        self.top_bar_widget2 = top_bar_widget2
        # Кнопки этой панели
        self.top_bar_widget2.pushButtonGoBack.clicked.connect(self.button_back_clicked)
        self.top_bar_widget2.pushButtonImport.clicked.connect(self.import_file)
        self.top_bar_widget2.pushButtonExport.clicked.connect(self.export_file)

        # Элемент предназначенный для ввода слов в словарь
        self.wordInput.pushButtonAddToDictionary.clicked.connect(self.button_add_word_clicked)
        self.wordInputAnimation = QtCore.QPropertyAnimation(self.wordInput, b'maximumHeight')
        self.wordInputAnimation.setEasingCurve(QtCore.QEasingCurve.OutBack)

        # Список тем
        self.listWidget_titles.itemSelectionChanged.connect(self.item_selected)
        self.listWidget_titles.setUniformItemSizes(True)
        # Список слов
        self.listWidget_words.setSpacing(2)
        self.listWidget_words.setUniformItemSizes(True)

        self.selected_item_index = None  # Индекс выбранного элемента в списке тем
        self.top_panel_page = 1  # Страница верхней панели
        self.current_title = None  # Выбранная тема. Используется для отображения слов во втором окне
        self.button_to_label = dict()  # Соответствие кнопки к заголовку темы
        self.button_to_index = dict()  # Порядковый номер кнопки удаления слов
        self.button_speech_to_word = dict()  # Слово, соответствующее кнопке озвучивания
        self.db_helper = Helper()  # Помощник по бд

        self.theme_titles = []  # Список заголовков тем
        self.words = []  # Список слов определенной тем
        self.translations = []  # Список переводов слов определенной темы

        self.list_widget1_set_data()  # Заполнение списка тем

    # Добавление новой темы
    def button_add_theme_clicked(self):
        # Вызов диалогового окна ввода и получение результата
        dialog = InputDialog()
        value = dialog.get_text()
        if value is not None and len(value) != 0:  # Проверка входных данных
            # Если тема уже содержится в базе данных, то к названию добавляется цифра
            if value in self.theme_titles:
                c = 2
                while True:
                    if value + '_' + str(c) not in self.theme_titles:
                        value = value + '_' + str(c)
                        break
                    c += 1
            # Добавление в базу данных и обновление списка
            self.db_helper.add_title(value)
            self.list_widget1_set_data()

    # Заполнение списка тем
    def list_widget1_set_data(self):
        self.listWidget_titles.clear()
        self.button_to_label.clear()
        # Обновление списка тем
        self.theme_titles = self.db_helper.get_titles()
        # Заполнение списка элементами
        for i in range(len(self.theme_titles)):
            item = QtWidgets.QListWidgetItem()
            item.setData(0, i)
            wdt = DictionaryTitle()
            wdt.pushButton.setText(f'Перейти')
            wdt.pushButton.clicked.connect(self.button_goto_theme_clicked)
            wdt.label.setText(str(self.theme_titles[i]))
            self.button_to_label[wdt.pushButton] = str(self.theme_titles[i])
            item.setSizeHint(wdt.sizeHint())
            self.listWidget_titles.addItem(item)
            self.listWidget_titles.setItemWidget(item, wdt)

    # Удаление темы
    def button_delete_theme_clicked(self):
        # Проверка на наличие выбранного элемента
        if self.selected_item_index is not None:
            # Подтверждение удаления
            confirmation = ConfirmationDialog(
                "Действительно удалить?").get_results()  # Вызов диалогового окна
            if not confirmation:
                return
            # Удаление из базы данных и обновление списка
            self.db_helper.delete_title(self.theme_titles[self.selected_item_index])
            self.list_widget1_set_data()
            self.selected_item_index = None

    # Переход к выбранной теме
    def button_goto_theme_clicked(self):
        self.stackedWidget.setCurrentIndex(1)  # Переключение страницы основного окна
        self.stacked_widget_top_bar.setCurrentIndex(2)  # Переключение страницы верхней панели
        self.word_input_animation(True)  # Анимация поля ввода слов
        self.top_panel_page = 2
        self.current_title = (self.button_to_label[self.sender()])
        # Установка элементов в список
        self.list_widget2_set_data_words()

    # Переход со словаря к списку тем
    def button_back_clicked(self):
        self.stackedWidget.setCurrentIndex(0)
        self.stacked_widget_top_bar.setCurrentIndex(1)
        self.word_input_animation(False)  # Обратная анимация поля ввода слов
        self.top_panel_page = 1
        self.current_title = None

    # Добавление слова в словарь
    def button_add_word_clicked(self):
        # Считывание данных с полей ввода
        word = self.wordInput.lineEditWord.text().strip()
        translation = self.wordInput.lineEditTranslation.text().strip()
        # Проверка введенных данных
        if len(word.strip()) != 0 and len(translation) != 0:
            # Проверка на содержание в базе данных
            if word not in self.words and translation not in self.translations:
                # Добавление в базу данных
                self.db_helper.add_word(self.current_title, word, translation)
                # Очистка полей ввода
                self.wordInput.lineEditWord.setText('')
                self.wordInput.lineEditTranslation.setText('')
                # Обновление списка и прокручивание его вниз
                self.list_widget2_set_data_words()
                self.listWidget_words.scrollToBottom()

    # Заполнение словаря
    def list_widget2_set_data_words(self):
        # Обновление списков
        self.words, self.translations = self.db_helper.get_words_by_title(self.current_title)
        self.listWidget_words.clear()
        for i in range(len(self.words)):
            item = QtWidgets.QListWidgetItem()
            item.setData(0, i + 1)
            wdt = DictionaryPair()
            wdt.label.setText(str(i + 1))
            wdt.label_2.setText(self.words[i])
            wdt.label_3.setText(self.translations[i])
            item.setSizeHint(wdt.sizeHint())
            self.button_to_index[wdt.pushButton] = i
            self.button_speech_to_word[wdt.pushButtonSpeech] = self.words[i]
            wdt.pushButton.clicked.connect(self.button_delete_word_clicked)
            wdt.pushButtonSpeech.clicked.connect(self.voice)
            self.listWidget_words.addItem(item)
            self.listWidget_words.setItemWidget(item, wdt)

    # Удаление слова из словаря
    def button_delete_word_clicked(self):
        index = self.button_to_index[self.sender()]  # Номер нажатой кнопки
        # Удаление из базы данных и обновление списка
        self.db_helper.delete_word(self.current_title, self.words[index], self.translations[index])
        self.list_widget2_set_data_words()

    # Сохранение номера выбранного элемента списка тем
    def item_selected(self):
        if len(self.listWidget_titles.selectedItems()):
            self.selected_item_index = self.listWidget_titles.selectedItems()[0].data(0)

    # Анимация поля ввода слов
    def word_input_animation(self, direction):
        self.wordInputAnimation.setStartValue(0 if direction else 55)
        self.wordInputAnimation.setEndValue(55 if direction else 0)
        self.wordInputAnimation.setDuration(250)
        self.wordInputAnimation.start()

    def voice(self):
        threading.Thread(
            target=self.text_to_speech, args=(self.sender(),), daemon=True
        ).start()

    # Озвучивание слова
    def text_to_speech(self, sender):
        self.engine.say(self.button_speech_to_word[sender])
        try:
            self.engine.runAndWait()
        except RuntimeError:
            pass

    # Ввод данных в словарь из
    def import_file(self):
        # Выбор файла
        filename = QtWidgets.QFileDialog.getOpenFileName(
            self, 'Выбрать файл', '', '(*.txt);; Все файлы (*)')[0]
        # Проверка названия файла на пустоту
        if len(filename) == 0:
            return
        file = open(filename, encoding='utf8')
        data = file.read()  # Чтение данных из файла
        file.close()

        # Проверка содержимого файла на пустоту
        if len(data.splitlines()) == 0:
            info_dialog = InfoDialog('Выбранный файл пуст. Изменения не внесены')
            info_dialog.exec_()
            return

        f_words, f_translations = [], []  # Данные из файла
        c = 1
        # Построчное чтение файла
        for line in data.splitlines():
            try:
                if '"' in line:
                    raise IncorrectSymbol
                f_word, f_translation = line.split(';')  # Чтение слова и перевода слова из строки
            except ValueError:  # В случае ошибки вызывается диалоговое окно с информацией
                info_dialog = InfoDialog(f'Ошибка в строке {c}. Изменения не внесены')
                info_dialog.exec_()
                return
            except IncorrectSymbol:
                info_dialog = InfoDialog(
                    f'Недопустимый символ (") в строке {c}. Изменения не внесены')
                info_dialog.exec_()
                return
            except Exception:
                info_dialog = InfoDialog(f'Ошибква в файле')
                info_dialog.exec_()
                return
            f_words.append(f_word)
            f_translations.append(f_translation)
            c += 1

        # Подтверждение
        confirmation_dialog = ConfirmationDialog('Данные словаря будут переписаны.\nПродолжить?')
        result = confirmation_dialog.get_results()

        # Выход из функции при отклонении подтверждения
        if not result:
            return

        # Удаление старых слов из базы данных
        for word, translation in zip(self.words, self.translations):
            self.db_helper.delete_word(self.current_title, word, translation)
        # Внесение новых слов в базу данных
        for word, translation in zip(f_words, f_translations):
            self.db_helper.add_word(self.current_title, word, translation)
        # Обновление списка
        self.list_widget2_set_data_words()

    # Сохранение словаря в файл
    def export_file(self):
        # Выбор директории
        pathname = QtWidgets.QFileDialog.getExistingDirectory()
        # Выход из функции если директории нет
        if len(pathname) == 0:
            return

        files = listdir(pathname)  # Список всех файлов в выбранной директории
        filename = self.current_title  # Название сохраняемого файла
        # Проверка на наличие файла с таким же названием в выбранной директории
        if filename + '.txt' in files:
            c = 2
            # Добавление числа к файлу, до тех пор, пока название не окажется свободным
            while True:
                if filename + '_' + str(c) + '.txt' not in files:
                    break
                c += 1
            filename = filename + '_' + str(c)
        filename += '.txt'
        file = open(filename, 'a', encoding='utf8')
        for word, translation in zip(self.words, self.translations):
            file.write(
                word + ";" + translation + "\n")  # Построчная запись в файл в виде слово;перевод
        file.close()

class IncorrectSymbol(Exception):
    pass


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

import sys
import os
import numpy as np
import pandas as pd
from scipy.stats import entropy
from sklearn import naive_bayes
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.metrics import precision_recall_fscore_support
import time
import datetime
import glob

from tqdm import tqdm
import warnings
from sklearn.utils import resample
from pandas.api.types import is_string_dtype
from pandas.api.types import is_numeric_dtype, is_categorical_dtype
import math
from itertools import combinations
import random
import shutil
import argparse
warnings.filterwarnings("ignore")

from sklearn import tree
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC, LinearSVC
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier
from keras.models import Sequential
from keras.layers import Dense
from keras import backend as K
from sklearn.metrics import accuracy_score
from sklearn.decomposition import PCA
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_breast_cancer

import tensorflow as tf
import gc
gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
for gpu in gpus:
    tf.config.experimental.per_process_gpu_memory_fraction = 0.7
from modules.data_preprocessing import DataPreprocessing

class TriTraining:
    def __init__(self, base_classifier_str = [], X_train = [], y_train = [], X_unlabeled = [], data_pre_type = "", confidence_threshold = None):
        self.setting_dict = {
        "NB" : "naive_bayes.GaussianNB()",
        "AdaBoost" : "AdaBoostClassifier(algorithm='SAMME.R', base_estimator=None, learning_rate=0.6,n_estimators=30, random_state=0)",
        "SVM" : """make_pipeline(StandardScaler(),SVC(C=0.6, break_ties=False, cache_size=2000, class_weight=None, coef0=0.0,
            decision_function_shape='ovr', degree=3, gamma=0.001, kernel='linear',
            max_iter=100, probability=True,random_state=None, shrinking=True,
            tol=0.001, verbose=False))""",
        "RF" : """RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,
                                       criterion='gini', max_depth=None,
                                       max_leaf_nodes=None, max_samples=None,
                                       min_impurity_decrease=0.0, min_impurity_split=None,
                                       min_samples_leaf=1, min_samples_split=2,
                                       min_weight_fraction_leaf=0.0, n_estimators=60,
                                       n_jobs=None, oob_score=False, random_state=None,
                                       verbose=0, warm_start=False)""",
        "KNN" : "KNeighborsClassifier(n_neighbors = 10)",
        "DT" : "DecisionTreeClassifier(criterion = 'entropy')"
        }
        self.confidence_threshold = confidence_threshold
        self.threshold_dict = {}
        self.data_pre = DataPreprocessing()
        self.data_pre_type = data_pre_type
        if base_classifier_str != []:
            self.base_classifiers_str = base_classifier_str
            self.feature_pre_models = [0] * len(self.base_classifiers_str)
            self.X_labeled = X_train
            self.y_labeled = y_train
            self.unlabeled_num_sum = 0
            self.unlabeled_nums = [0] * len(self.base_classifiers_str)
            self.turn_nums = [0] * len(self.base_classifiers_str)
            self.X_unlabeled = X_unlabeled
            self.X_combine = []
            self.y_combine = []
            self.X_Si, self.y_Si = self.boostrap_sample()
            self.base_classifiers = self.init_classifier()

    def init_classifier(self):
        result = []
        for i in range(len(self.base_classifiers_str)):
            model = self.different_fit(i, self.X_Si[i], self.y_Si[i])
            result.append(model)
        return result

    def boostrap_sample(self):
        X_Si = []
        y_Si = []
        for i in range(len(self.base_classifiers_str)):
            X_sample, y_sample = resample(self.X_labeled, self.y_labeled, stratify = self.y_labeled)
            X_Si.append(X_sample)
            y_Si.append(y_sample)
            self.X_combine.append(X_sample)
            self.y_combine.append(y_sample)
        return X_Si, y_Si

    def different_fit(self, i, X, y):
        if self.base_classifiers_str[i] in ["NB", "AdaBoost", "XgBoost", "SVM", "RF", "KNN", "DT"]:
            model = eval(self.setting_dict[self.base_classifiers_str[i]])
            model.fit(X, y)
            return model
        else:
            pass

    def different_predict(self, i, X):
        if self.base_classifiers_str[i] in ["NB", "AdaBoost", "XgBoost", "SVM", "RF", "KNN", "DT"]:
            predict = self.base_classifiers[i].predict(X)
            predict = predict.tolist()
            return predict
        else:
            pass

    def different_predict_proba(self, i, X):
        if self.base_classifiers_str[i] in ["NB", "AdaBoost", "XgBoost", "SVM", "RF", "KNN", "DT"]:
            predict = self.base_classifiers[i].predict_proba(X)
            predict = predict.tolist()
            return predict
        else:
            pass

    def remove_Li(self, X, y, remove_num):
        X = np.array(X)
        y = np.array(y)
        remove_list = np.random.choice(len(X), replace = False, size = min(len(X), remove_num))
        X = np.delete(X, remove_list, axis = 0)
        y = np.delete(y, remove_list, axis = 0)
        return X, y

    def subsample(self, pre_error_rate, pre_Li_ln, error_rate, X_Li, y_Li):
        s = math.ceil(pre_error_rate * pre_Li_ln / error_rate - 1)
        X_Li, y_Li = self.remove_Li(X_Li, y_Li, len(X_Li) - s)
        return X_Li, y_Li

    def measure_error(self, i):
        """
        error rate = number of labeled data both classidiers make incorrect classification /
        number of labeled data the classifiers(expect classifier i) have the same prediction
        """
        error_up = 0
        error_down = 0.00001
        predicts = [[] for ele in range(len(self.base_classifiers))]
        for j in range(len(self.base_classifiers)):
            if j != i:
                predicts[j] = self.different_predict(j, self.X_labeled)
        for k in range(len(self.y_labeled)):
            temp = []
            for j in range(len(self.base_classifiers)):
                if j != i:
                    temp.append(predicts[j][k])
            temp = list(set(temp))
            if len(temp) == 1:
                error_down += 1
                if temp[0] != self.y_labeled.iloc[k]:
                    error_up += 1
        return error_up / error_down

    def generate_confidence_table(self, indexs):
        predicts = []
        bFR = True
        for index in indexs:
            predict = self.different_predict_proba(index, self.X_labeled)
            predict = pd.DataFrame(predict)
            if bFR:
                predicts = predict
                bFR = False
            else:
                predicts = pd.concat([predicts, predict], axis = 1)
        return predicts

    def retrain(self, i, X_Li, y_Li):
        model = eval(self.setting_dict[self.base_classifiers_str[i]])
        X_Li = pd.DataFrame(X_Li, columns = self.X_labeled.columns)
        X_combine = self.X_labeled.append(X_Li)
        self.X_combine[i] = X_combine
        y_Li = pd.Series(y_Li, dtype = "category")
        y_Li.cat.set_categories(self.y_labeled.cat.categories, inplace = True)
        y_combine = self.y_labeled.append(y_Li)
        self.y_combine[i] = y_combine
        model = self.different_fit(i, X_combine, y_combine)
        return model

    def train(self):
        bFR = True
        #initialize error rate, previous error rate, and previous length of Li
        pre_error_rate = [0.5 for ele in range(len(self.base_classifiers))]
        error_rate = [0.0 for ele in range(len(self.base_classifiers))]
        pre_Li_ln = [0.0 for ele in range(len(self.base_classifiers))]
        turn_start_time = time.time()
        turn = 0
        while True:
            X_Li = [[] for ele in range(len(self.base_classifiers))]
            y_Li = [[] for ele in range(len(self.base_classifiers))]
            update = [False for ele in range(len(self.base_classifiers))]
            for i in range(len(self.base_classifiers)):
                #use classifiers(expect classifier i) to measure error
                error_rate[i] = self.measure_error(i)
                if error_rate[i] < pre_error_rate[i]:
                    predicts = [[] for ele in range(len(self.base_classifiers))]
                    #use classifiers(expect classifier i) to predict unlabeled data
                    for j in range(len(self.base_classifiers)):
                        if j != i:
                            predicts[j] = self.different_predict(j, self.X_unlabeled)

                    #if two classifiers have the same prediction for unlabeled data: add it to Li
                    for k in range(len(self.X_unlabeled)):
                        temp = []
                        for j in range(len(self.base_classifiers)):
                            if j != i:
                                temp.append(predicts[j][k])
                        temp = list(set(temp))
                        if len(temp) == 1:
                            X_Li[i].append(self.X_unlabeled.iloc[k,:])
                            y_Li[i].append(temp[0])

                    if pre_Li_ln[i] == 0:
                        pre_Li_ln[i] = math.floor(error_rate[i] / (pre_error_rate[i] - error_rate[i]) + 1)
                    """
                    if error rate * length of Li < previous error rate * length of Li:
                        use labeled data and unlabeled data Li to retrain classifiers
                    elif length of Li is too big:
                        use subsampling to reduce the size of Li
                        use labeled data and unlabeled data Li to retrain classifiers
                    """
                    if pre_Li_ln[i] < len(X_Li[i]):
                        if error_rate[i] * len(X_Li[i]) < pre_error_rate[i] * pre_Li_ln[i]:
                            update[i] = True
                            self.turn_nums[i] += 1
                            self.unlabeled_nums[i] += len(X_Li[i])
                        elif pre_Li_ln[i] > error_rate[i] / (pre_error_rate[i] - error_rate[i]):
                            X_Li[i], y_Li[i] = self.subsample(pre_error_rate[i], pre_Li_ln[i], error_rate[i], X_Li[i], y_Li[i])
                            update[i] = True
                            self.turn_nums[i] += 1
                            self.unlabeled_nums[i] += len(X_Li[i])
                        else:
                            pass
            turn += 1
            # if all classifiers don't update this turn: stop the algorithm
            if True not in update:
                turn_end_time = time.time()
                total_time = str(round((turn_end_time - turn_start_time) / 60,1))
                for i in range(len(self.unlabeled_nums)):
                    if self.unlabeled_nums[i] != 0:
                        self.unlabeled_num_sum += self.unlabeled_nums[i] / self.turn_nums[i]
                break
            for i in range(len(self.base_classifiers)):
                if update[i]:
                    self.base_classifiers[i] = self.retrain(i, X_Li[i], y_Li[i])
                pre_error_rate[i] = error_rate[i]
                pre_Li_ln[i] = len(X_Li)

    def select_max_category(self, vote_dict, proba_dict):
        max_vote = max(vote_dict.items(), key = lambda ele : ele[1])[1]
        max_categories = []
        for key, value in vote_dict.items():
            if value == max_vote:
                max_categories.append(key)
        if len(max_categories) > 1:
            compare_list = []
            for category in max_categories:
                compare_list.append([category, proba_dict[category]])
            return max(compare_list, key = lambda ele : ele[1])[0]
        else:
            return max_categories[0]

    def majority_vote(self, X_test):
        predicts = []
        predicts_proba = []
        y_pred = []
        for i in range(len(self.base_classifiers)):
            predicts.append(self.different_predict(i, X_test))
            predicts_proba.append(self.different_predict_proba(i, X_test))

        for k in range(len(predicts[0])):
            count_dict = {}
            proba_dict = {}
            for i in range(len(predicts)):
                temp = count_dict.get(predicts[i][k])
                if temp != None:
                    count_dict[predicts[i][k]] += 1
                else:
                    count_dict.update({predicts[i][k]:1})
                temp = proba_dict.get(predicts[i][k])
                if temp != None:
                    proba_dict[predicts[i][k]] += predicts_proba[i][k]
                else:
                    proba_dict.update({predicts[i][k] : predicts_proba[i][k]})
            y_pred.append(self.select_max_category(count_dict, proba_dict))
        return y_pred

    def evaluate(self, X_test, y_test, avg_type = "weighted", save_pred = True):
        y_pred = self.majority_vote(X_test)
        y_test = y_test.tolist()
        results = []
        precision, recall, fscore, support = precision_recall_fscore_support(y_test, y_pred, average = avg_type)
        score_dict = {}
        score_dict.update({"precision":precision})
        score_dict.update({"recall":recall})
        score_dict.update({"fscore":fscore})
        score_dict.update({"support":support})
        return score_dict, y_pred, self.unlabeled_num_sum

if __name__ == "__main__":
    #algorithm parameters
    parser = argparse.ArgumentParser()
    parser.add_argument("-g", "--groups", type = str, nargs = "+",
                        choices = ["NB", "SVM", "RF", "AdaBoost", "KNN", "DT"],
                        default = ["NB"],
                        help = "the three base classifiers use the same type of classifier")
    parser.add_argument("-t", "--train_size", type = float,
                        default = 0.75,
                        help = "the size of the training dataset")
    parser.add_argument("-l", "--label_rates", type = float, nargs = "+",
                        default = [0.1],
                        help = "the size of the labeled data = train_size * label_rate")
    parser.add_argument("-e", "--experiment_num", type = int,
                        default = 10,
                        help = "number of experiments")
    #data and log parameters
    parser.add_argument("--data_pre_type", type = str,
                        choices = ["all_category", "category_and_numeric"],
                        default = "category_and_numeric",
                        help = """
                        category => 10 equal-width bins
                        numeric => log
                        """)
    parser.add_argument("--data_dir", type = str,
                        default = None,
                        help = """
                        if data_dir == None: read data from sklearn.datasets package
                        else: read csv data from data directory
                        """)
    parser.add_argument("--log_dir", type = str,
                        default = os.path.join(os.getcwd(), "log", "tri_training_" + time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())),
                        help = "log directory")
    parser.add_argument("--avg_types", type = str, nargs = "+",
                        choices = ["micro", "macro", "weighted"],
                        default = ["weighted"],
                        help = """
                        micro => micro average
                        macro => macro average
                        weighted => weighted macro average
                        """)
    parser.add_argument("--save_pred",
                        action = "store_true",
                        help = "whether to save prediction and number of unlabeled")
    args = parser.parse_args()
    os.mkdir(args.log_dir)
    for i in range(len(args.groups)):
        args.groups[i] = [args.groups[i]] * 3
    data_pre = DataPreprocessing()
    total_start_time = time.strftime("%Y/%m/%d, %H:%M:%S", time.localtime())
    if args.data_dir == None:
        print("dataset start : breast_cancer_w")
        dataset_start = datetime.datetime.now()
        if args.save_pred:
            log_dict = {
            "dataset":[],"group":[],"confidence_threshold":[],"label_rate":[],"avg_type":[],"precision":[],"recall":[],"fscore":[],"cv":[],
            "y_pred":[],"y_test":[],"unlabeled_num":[]
            }
        else:
            log_dict = {
            "dataset":[],"group":[],"confidence_threshold":[],"label_rate":[],"avg_type":[],"precision":[],"recall":[],"fscore":[],"cv":[]
            }
        dataset = load_breast_cancer()
        X = dataset["data"]
        y = dataset["target"]
        X = pd.DataFrame(X)
        y = pd.Series(y)
        if args.data_pre_type == "category_and_numeric":
            X = data_pre.category_and_numeric(X)
        elif args.data_pre_type == "all_category":
            X = data_pre.columns_category(X)
        else:
            print("data_pre_type error!")
        X = pd.get_dummies(X)
        y = data_pre.to_category(y)
        bEnd = False
        for ele in range(args.experiment_num):
            print("experiment_num start : " + str(ele))
            experiment_num_start = datetime.datetime.now()
            for label_rate in args.label_rates:
                X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = args.train_size, stratify = y, random_state = ele)
                X_labeled, X_unlabeled, y_labeled, y_unlabeled = train_test_split(X_train, y_train, train_size = label_rate, stratify = y_train, random_state = ele)
                for group in args.groups:
                    print("group start : " + "_".join(group))
                    group_start = datetime.datetime.now()
                    confidence_threshold = None
                    tri_training = TriTraining(group, X_labeled, y_labeled, X_unlabeled, args.data_pre_type,
                    confidence_threshold = confidence_threshold
                    )
                    print("train start : " + time.strftime("%Y/%m/%d, %H:%M:%S", time.localtime()))
                    train_start = datetime.datetime.now()
                    tri_training.train()
                    train_end = datetime.datetime.now()
                    print("seconds : " + str((train_end - train_start).seconds))
                    print("train end : " + time.strftime("%Y/%m/%d, %H:%M:%S", time.localtime()))
                    for avg_type in args.avg_types:
                        score_dict, y_pred, unlabeled_num = tri_training.evaluate(X_test, y_test, avg_type)
                        log_dict["dataset"].append("breast_cancer_w")
                        log_dict["group"].append("_".join(group))
                        log_dict["confidence_threshold"].append(-1)
                        log_dict["label_rate"].append(label_rate)
                        log_dict["avg_type"].append(avg_type)
                        log_dict["precision"].append(score_dict["precision"])
                        log_dict["recall"].append(score_dict["recall"])
                        log_dict["fscore"].append(score_dict["fscore"])
                        log_dict["cv"].append(-1)
                        if args.save_pred:
                            log_dict["y_pred"].append(y_pred)
                            log_dict["y_test"].append(y_test.tolist())
                            log_dict["unlabeled_num"].append(unlabeled_num)
                    group_end = datetime.datetime.now()
                    print("seconds : " + str((group_end - group_start).seconds))
                    print("group end : " + "_".join(group))
            experiment_num_end = datetime.datetime.now()
            print("seconds : " + str((experiment_num_end - experiment_num_start).seconds))
            print("experiment_num end")
            if bEnd:
                break
        if not bEnd:
            data_name = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) + "_" + "breast_cancer_w.csv"
            df_log = pd.DataFrame(log_dict)
            df_log.to_csv(os.path.join(args.log_dir, data_name), index = False)
        dataset_end = datetime.datetime.now()
        print("seconds: " + str((dataset_end - dataset_start).seconds))
        print("dataset end")
        print("---")
    else:
        for data in glob.glob(os.path.join(args.data_dir, "*.csv")):
            dataset_start = datetime.datetime.now()
            print("dataset start : " + os.path.basename(data))
            if args.save_pred:
                log_dict = {
                "dataset":[],"group":[],"confidence_threshold":[],"label_rate":[],"avg_type":[],"precision":[],"recall":[],"fscore":[],"cv":[],
                "y_pred":[],"y_test":[],"unlabeled_num":[]
                }
            else:
                log_dict = {
                "dataset":[],"group":[],"confidence_threshold":[],"label_rate":[],"avg_type":[],"precision":[],"recall":[],"fscore":[],"cv":[]
                }
            df = pd.read_csv(data)
            if args.data_pre_type == "category_and_numeric":
                df = data_pre.category_and_numeric(df)
            elif args.data_pre_type == "all_category":
                df = data_pre.columns_category(df)
            else:
                print("data_pre_type error!")
            X = df[df.columns[:-1]]
            X = pd.get_dummies(X)
            y = df[df.columns[-1]]
            bEnd = False
            for ele in range(args.experiment_num):
                print("experiment_num start : " + str(ele))
                experiment_num_start = datetime.datetime.now()
                for label_rate in args.label_rates:
                    X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = args.train_size, stratify = y, random_state = ele)
                    X_labeled, X_unlabeled, y_labeled, y_unlabeled = train_test_split(X_train, y_train, train_size = label_rate, stratify = y_train, random_state = ele)
                    for group in args.groups:
                        print("group start:" + "_".join(group))
                        group_start = datetime.datetime.now()
                        confidence_threshold = None
                        tri_training = TriTraining(group, X_labeled, y_labeled, X_unlabeled, args.data_pre_type,
                        confidence_threshold = confidence_threshold
                        )
                        print("train start : " + time.strftime("%Y/%m/%d, %H:%M:%S", time.localtime()))
                        train_start = datetime.datetime.now()
                        tri_training.train()
                        train_end = datetime.datetime.now()
                        print("seconds : " + str((train_end - train_start).seconds))
                        print("train end : " + time.strftime("%Y/%m/%d, %H:%M:%S", time.localtime()))
                        for avg_type in args.avg_types:
                            score_dict, y_pred, unlabeled_num = tri_training.evaluate(X_test, y_test, avg_type)
                            log_dict["dataset"].append(os.path.basename(data))
                            log_dict["group"].append("_".join(group))
                            log_dict["confidence_threshold"].append(-1)
                            log_dict["label_rate"].append(label_rate)
                            log_dict["avg_type"].append(avg_type)
                            log_dict["precision"].append(score_dict["precision"])
                            log_dict["recall"].append(score_dict["recall"])
                            log_dict["fscore"].append(score_dict["fscore"])
                            log_dict["cv"].append(-1)
                            if args.save_pred:
                                log_dict["y_pred"].append(y_pred)
                                log_dict["y_test"].append(y_test.tolist())
                                log_dict["unlabeled_num"].append(unlabeled_num)
                        group_end = datetime.datetime.now()
                        print("seconds : " + str((group_end - group_start).seconds))
                        print("group end : " + "_".join(group))
                experiment_num_end = datetime.datetime.now()
                print("seconds : " + str((experiment_num_end - experiment_num_start).seconds))
                print("experiment_num")
                if bEnd:
                    break
            if not bEnd:
                data_name = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime()) + "_" + os.path.basename(data)
                df_log = pd.DataFrame(log_dict)
                df_log.to_csv(os.path.join(args.log_dir, data_name), index = False)
        dataset_end = datetime.datetime.now()
        print("seconds: " + str((dataset_end - dataset_start).seconds))
        print("dataset end")
total_end_time = time.strftime("%Y/%m/%d, %H:%M:%S", time.localtime())
print("finish!")
print(total_start_time)
print(total_end_time)


# 연산자 끼워넣기 210928
"""
입력
N 첫줄 수의 개수
둘째 줄에는 A1....An 의 배열이 주어진다
셋째 줄에는 합이 N-1인 정수 4개가 주어지는데 차례대로 +, -, *, /의 개수이다. (나눗셈은 몫만 취한다.)

샛째 줄의 연산자들을 활용해서 최댓값, 최솟값을 만들어서 출력하자

* 알고리즘, 주의사항
나눗셈의 경우 몫만 취한다고 했는데 음수의 경우도 생각해야 한다.
이로인해 // 연산자를 직접 사용하는것 보다 int(a/b) 함수를 사용하는게 더 낫다
"""
# 블로그에서 본 재귀 함수
from itertools import permutations
def cal(num, idx, add, sub, multi, division):
    global n, maxv, minv
    if idx == n:
        maxv = max(num, maxv)
        minv = min(num, minv)
        return
    else:
        if add:
            cal(num + num_list[idx], idx + 1, add-1, sub, multi, division)
        if sub:
            cal(num - num_list[idx], idx + 1, add, sub-1, multi, division)
        if multi:
            cal(num * num_list[idx], idx + 1, add, sub, multi-1, division)
        if division:
            cal(int(num/num_list[idx]), idx + 1, add, sub, multi, division-1)

if __name__ == "__main__":
    maxv = -float('inf')
    minv = float('inf')

    n = int(input())
    num_list = list(map(int, input().split()))
    add, sub, mul, div = map(int, input().split())
    cal(num_list[0], 1, add, sub, mul, div)
    print(maxv)
    print(minv)

# 답지에 있던 dfs 함수 (비슷한데 약간 달라서 복붙)
n = int(input())
data = list(map(int, input().split()))
add, sub, mul, div = map(int, input().split())

min_v = 1e9
max_v = -1e9

def dfs(i, now):
    global min_v, max_v, add, sub, mul, div
    if i == n:
        min_v = min(min_v, now)
        max_v = max(max_v, now)
    else:
        if add > 0:
            add -= 1
            dfs(i+1, now+data[i])
            add += 1
        if sub > 0:
            sub -= 1
            dfs(i+1, now - data[i])
            sub += 1
        if mul > 0:
            mul -= 1
            dfs(i+1, int(now * data[i]))
            mul += 1
        if div > 0:
            div -= 1
            dfs(i+1, int(now / data[i]))
            div += 1

dfs(1, data[0])

print(max_v)
print(min_v)

# permutation을 활용한 답지, 숏코딩
N = int(input())
A = list(map(int, input().split()))
a, s, m, d = map(int, input().split())

rs = list()
for p in set(permutations('+'*a+'-'*s+'*'*m+'/'*d)):
    r = A[0]
    for i in range(1, N):
        r = {'+': r+A[i], '-': r-A[i], '*': r*A[i], '/': int(r/A[i])}[p[i-1]]

    rs.append(r)

print(max(rs), min(rs))


"""empty message

Revision ID: 55feb17be620
Revises: 1ce7e0de1096
Create Date: 2022-05-23 11:12:56.176505

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '55feb17be620'
down_revision = '1ce7e0de1096'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('group_memberships', sa.Column('created_at', sa.DateTime(), nullable=False))
    op.add_column('groups', sa.Column('created_at', sa.DateTime(), nullable=False))
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('groups', 'created_at')
    op.drop_column('group_memberships', 'created_at')
    # ### end Alembic commands ###


#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from flask import Flask, request, render_template
from cloudant.account import Cloudant
from cloudant.result import Result
import hashlib
import time

# Connectivity to cloudant
USERNAME = ''
PASSWORD = ''
ACCOUNT_URL = ''

client = Cloudant(USERNAME, PASSWORD, url=ACCOUNT_URL)
client.connect()
session = client.session()

# Open the database
my_database = client['files']

app = Flask(__name__)

@app.route('/')
def Welcome():
    return app.send_static_file('index.html')

################ UPLOAD LOGIC #################
@app.route('/upload', methods=['POST', 'GET'])
def upload():
    if request.method == 'POST':
        version = 1
        file = request.files['file']
        hashcode = hashlib.md5(file.filename).hexdigest()
        content = file.read()

        for document in my_database:
            if document['fileName'] == file.filename:
                if document['hashValue'] == hashcode:
                    return 'File not Uploaded'
                else:
                    version = document['versionNo'] + 1

        # Create a document to be pushed cloudant
        data = {
        'id': file.filename+hashcode,
        'fileName': file.filename,
        'hashValue': hashcode,
        'versionNo':  version,
        'fileContent': content,
        'lastModifiedDate': time.ctime(os.path.getmtime(file.filename))
        }

        #Push the document to cloudant
        my_document = my_database.create_document(data)

        return 'File Uploaded'

################ FILE LIST LOGIC ################
@app.route('/list', methods=['POST', 'GET'])
def list():
    list = ''
    for document in my_database:
        list = list + document['fileName'] + '_' + str(document['versionNo'])
        list = list + "       Download       "
        list = list + "Delete
"
    return '''<!DOCTYPE html>

    <title>Python Flask Application</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="static/stylesheets/style.css">

    ''' + list + '''

'''

################ DOWNLOAD LOGIC #################
@app.route('/download', methods=['POST', 'GET'])
def download():
    docID = request.args.get('id')
    document = my_database[docID]
    fname = document['fileName'] + '_' + str(document['versionNo'])
    downloadFile = open(fname,'w')
    downloadFile.write(document['fileContent'])
    return 'File Download Successful'

################ DELETE LOGIC #################
@app.route('/delete', methods=['POST', 'GET'])
def delete():
    docID = request.args.get('id')
    document = my_database[docID]
    document.delete()
    return 'File Delete Successful'

port = os.getenv('VCAP_APP_PORT', '5000')
if __name__ == "__main__":
	app.run(host='0.0.0.0', port=int(port), debug=True)


from rest_framework import serializers
from api.models import Client, Contract, Event
from core.models import Employee
from core.serializers import EmployeeSerializer

class ClientSerializer(serializers.ModelSerializer):
    """
    ModelSerializer that serializes Client instances.
    """
    sales_contact = EmployeeSerializer(read_only=True)

    class Meta:
        model = Client
        fields = ['id',
                  'first_name',
                  'last_name',
                  'email',
                  'phone',
                  'mobile',
                  'company_name',
                  'sales_contact',
                  'is_prospect',
                  'created_at',
                  'updated_at',
                  'date_revokated']

    def create(self, validated_data):
        sales_contact = self.context.get("request", None).user

        client = Client.objects.create(
            first_name=validated_data["first_name"],
            last_name=validated_data["last_name"],
            email=validated_data['email'],
            phone=validated_data['phone'],
            mobile=validated_data['mobile'],
            company_name=validated_data['company_name'],
            sales_contact=sales_contact,
            is_prospect=validated_data['is_prospect'],
            date_revokated=validated_data['date_revokated'],
        )
        client.save()
        return client

class ContractSerializer(serializers.ModelSerializer):
    """
    ModelSerializer that serializes Contract instances.
    """
    client = ClientSerializer(read_only=True)

    class Meta:
        model = Contract
        fields = ['id',
                  'client',
                  'date_signed',
                  'status',
                  'amount',
                  'payement_due',
                  'date_revokated']

    def create(self, validated_data):
        client = Client.objects.get(pk=self.context.get("view").kwargs["client_pk"])
        if client.is_prospect:
            raise serializers.ValidationError(
                "You can't create a contract to a client who is still a prospect"
                )
        else:
            contract = Contract.objects.create(
                client=client,
                date_signed=validated_data["date_signed"],
                status=validated_data["status"],
                amount=validated_data["amount"],
                payement_due=validated_data["payement_due"],
                date_revokated=validated_data['date_revokated'],
                )
            contract.save()
            return contract

class EventSerializer(serializers.ModelSerializer):
    """
    ModelSerializer that serializes Event instances.
    """
    contract = ContractSerializer(read_only=True)
    support_contact = serializers.SlugRelatedField(
        queryset=Employee.objects.filter(department='support'),
        slug_field='username',
    )

    class Meta:
        model = Event
        fields = ['id',
                  'name',
                  'contract',
                  'support_contact',
                  'attendees',
                  'start_date',
                  'end_date',
                  'notes',
                  'created_at',
                  'updated_at',
                  'date_revokated']

    def create(self, validated_data):
        contract = Contract.objects.get(pk=self.context.get("view").kwargs["contract_pk"])
        if not contract.status:
            raise serializers.ValidationError(
                "You can't create an event while a contract is still not signed"
                )
        else:
            event = Event.objects.create(
                name=validated_data["name"],
                contract=contract,
                support_contact=validated_data["support_contact"],
                attendees=validated_data["attendees"],
                start_date=validated_data["start_date"],
                end_date=validated_data["end_date"],
                notes=validated_data['notes'],
                date_revokated=validated_data['date_revokated'],
            )
            event.save()
            return event


import logging
import matplotlib.pyplot as plt
from dataclasses import dataclass

from eval_utils import parse_test_args, run_experiment, run_experiment_with_ui
from torchsim.core.datasets.dataset_se_base import SeDatasetSize
from torchsim.core.eval2.experiment import Experiment
from torchsim.core.eval2.experiment_controller import TrainTestComponentParams
from torchsim.core.eval2.experiment_runner_params import ExperimentParams
from torchsim.core.eval2.scaffolding import TopologyScaffoldingFactory
from torchsim.core.nodes import DatasetSeObjectsNode
from torchsim.research.experiment_templates2.task0_ta_analysis_template import Task0TaAnalysisTemplate, Task0TaAnalysisParams
from torchsim.research.research_topics.rt_2_1_2_learning_rate.node_groups.se_node_group import SeNodeGroup
from torchsim.research.research_topics.rt_3_7_1_task0_analysis.node_groups.dummy_model_group import DummyModelGroup
from torchsim.research.research_topics.rt_3_7_1_task0_analysis.topologies.task0_ta_analysis_topology import \
    Task0TaAnalysisTopology

logger = logging.getLogger(__name__)

@dataclass
class Params:
    experiment_params: Task0TaAnalysisParams
    train_test_params: TrainTestComponentParams

debug_params = Params(Task0TaAnalysisParams(measurement_period=1,
                                            num_classes=DatasetSeObjectsNode.label_size(),
                                            num_layers=2,  # has to be overwritten by actual num layers later
                                            sp_evaluation_period=2,
                                            show_conv_agreements=True),
                      TrainTestComponentParams(num_testing_phases=3,
                                               num_testing_steps=160,
                                               overall_training_steps=30))

middle_params = Params(Task0TaAnalysisParams(measurement_period=1,
                                             num_classes=DatasetSeObjectsNode.label_size(),
                                             num_layers=2,
                                             sp_evaluation_period=2),
                       TrainTestComponentParams(num_testing_phases=6,
                                                num_testing_steps=200,
                                                overall_training_steps=1500))

full_params = Params(Task0TaAnalysisParams(measurement_period=1,
                                           num_classes=DatasetSeObjectsNode.label_size(),
                                           num_layers=2,
                                           sp_evaluation_period=100),
                     TrainTestComponentParams(num_testing_phases=25,
                                              num_testing_steps=500,
                                              overall_training_steps=25000))

def run_measurement(name, topology_parameters, args, exp_pars):
    """"Runs the experiment with specified params, see the parse_test_args method for arguments"""

    scaffolding = TopologyScaffoldingFactory(Task0TaAnalysisTopology,
                                             se_group=SeNodeGroup,
                                             model=DummyModelGroup)

    template = Task0TaAnalysisTemplate("Task 0 layer-wise stats and classification accuracy",
                                       exp_pars.experiment_params,
                                       exp_pars.train_test_params)

    runner_parameters = ExperimentParams(max_steps=exp_pars.train_test_params.max_steps,
                                         save_cache=args.save,
                                         load_cache=args.load,
                                         clear_cache=args.clear,
                                         calculate_statistics=not args.computation_only,
                                         experiment_folder=args.alternative_results_folder)

    experiment = Experiment(template, scaffolding, topology_parameters, runner_parameters)

    logger.info(f'Running model: {name}')
    if args.run_gui:
        run_experiment_with_ui(experiment)
    else:
        run_experiment(experiment)

    if args.show_plots:
        plt.show()

def run_debug_comparison(args, num_conv_layers: int, exp_params):
    name = "Learning-rate-debug"

    cf_easy = [1, 2, 3, 4]
    size = SeDatasetSize.SIZE_24

    params = [
        {
            'se_group': {'class_filter': cf_easy,
                         'image_size': size},
            'model': {}
        },
        {
            'se_group': {'class_filter': cf_easy,
                         'image_size': size},
            'model': {}
        }
    ]

    exp_params.experiment_params.num_layers = num_conv_layers + 1  # needs to be there

    run_measurement(name, params, args, exp_params)

if __name__ == '__main__':
    arg = parse_test_args()

    run_debug_comparison(arg, 1, debug_params)


import _init_paths
import argparse
import glob
import os

import tensorflow as tf
from tensorflow.python.profiler.model_analyzer import Profiler
from tensorflow.python.profiler.model_analyzer import ALL_ADVICE
import numpy as np
from tqdm import tqdm

from config.ConfigParams import ConfigParams
from image.ImageUtils import ImageUtils
from model.TensorflowModel import TensorflowModel

def doParsing():
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
                                     description="TF Profiling script")
    parser.add_argument("--datasetTestDir", required=True, type=str, help="Dataset test directory")
    parser.add_argument("--configFile", required=True, type=str, help="Model config file")
    parser.add_argument("--modelPath", required=False, type=str, default="./export/graph.pb",
                        help="Filepath with trained model")
    parser.add_argument("--numImages", required=False, type=int, default=100,
                        help="Number of images to use during profiling")
    args = parser.parse_args()
    return args

def main():
    """
    Script to export TF profiling results
    following 1.13.1 up-to-date documentation https://www.tensorflow.org/api_docs/python/tf/profiler/Profiler
    You can use https://github.com/tensorflow/profiler-ui to view results.
    """
    args = doParsing()
    print(args)

    # Load config (it includes preprocessing type)
    config = ConfigParams(args.configFile)

    # Load model
    model = TensorflowModel(args.modelPath)

    print("Loaded model from " + args.modelPath)

    inputPlaceholder = model.getGraph().get_tensor_by_name(config.inputName + ":0")
    outputTensor = model.getGraph().get_tensor_by_name(config.outputName + ":0")

    profiler = Profiler(model.getGraph())

    options = tf.RunOptions(trace_level=tf.RunOptions.FULL_TRACE)
    run_metadata = tf.RunMetadata()

    # One by one image prediction forcing CPU usage
    with model.getSession() as sess:

        with tf.device("/gpu:0"):

            for ix, file in tqdm(enumerate(sorted(glob.glob(args.datasetTestDir + "/*.jpg"))[:args.numImages])):

                image = ImageUtils.loadImage(file)
                # Resize image and preprocess (inception or vgg preprocessing based on config)
                processedImage = ImageUtils.preprocessing(image=image, width=config.inputSize, height=config.inputSize,
                                                          preprocessingType=config.preprocessType,
                                                          meanRGB=config.meanRGB)

                # Convert colorspace
                processedImage = ImageUtils.convertImageFormat(processedImage, format=config.inputFormat)

                # Add 1st dimension for image index in batch
                processedImage = np.expand_dims(processedImage, axis=0)

                # Get and print TOP1 class
                result = sess.run(outputTensor, feed_dict={inputPlaceholder: processedImage},
                                  options=options, run_metadata=run_metadata)
                print(os.path.basename(file) + " -> " + str(np.argmax(result[0])))

                profiler.add_step(ix, run_metadata)

                # Profile the parameters of your model.
                #profiler.profile_name_scope(options=(tf.profiler.ProfileOptionBuilder
                #                                     .trainable_variables_parameter()))

                # Or profile the timing of your model operations.
                opts = tf.profiler.ProfileOptionBuilder.time_and_memory()
                profiler.profile_operations(options=opts)

                # Or you can generate a timeline:
                # opts = (tf.profiler.ProfileOptionBuilder(
                #     tf.profiler.ProfileOptionBuilder.time_and_memory())
                #         .with_step(ix)
                #         .with_timeline_output("timeline_step.json").build())
                #profiler.profile_graph(options=opts)

    os.makedirs(os.path.join('profiling', 'tfprof', config.architecture), exist_ok=True)
    # Auto detect problems and generate advice.
    profiler.advise(ALL_ADVICE)

    with open(os.path.join('profiling', 'tfprof', config.architecture, 'profiler.context'), 'wb') as f:
        f.write(profiler.serialize_to_string())

    print("Test finished")

if __name__ == '__main__':
    main()


import requests
from faker import Faker

URL = "http://localhost:8080"

def ping():
    r = requests.get(URL + "/ping")
    assert r.status_code == 200
    assert r.json() == {"message": "pong"}

def gen_location():
    points = [Faker().latlng() for _ in range(3)]
    points = list(map(lambda x: [float(x[0]), float(x[1])], points))
    location = {
        "name": Faker().word(),
        "description": Faker().sentence(),
        "points": points,
    }
    return dict(location)

def make_location():
    location = gen_location()
    r = requests.post(URL + "/location", json=location)
    print(r.json())
    assert r.status_code == 201
    return r.json()

def get_location(location_id):
    r = requests.get(URL + f"/location/{location_id}")
    assert r.status_code == 200
    return r.json()

def delete_location(location_id):
    r = requests.delete(URL + f"/location/{location_id}")
    assert r.status_code == 200

def get_locations():
    r = requests.get(URL + "/locations")
    assert r.status_code == 200
    return r.json()

def get_nearby_locations(lat, lon, radius):
    r = requests.get(URL + f"/locations/nearby?lat={lat}&lon={lon}&radius={radius}")
    assert r.status_code == 200
    return r.json()

def get_images(location_id):
    r = requests.get(URL + f"/location/{location_id}/images")
    assert r.status_code == 200
    return r.json()

def get_image(image_id):
    r = requests.get(URL + f"/image/{image_id}")
    assert r.status_code == 200
    content = r.content
    return content

def upload_image(location_id):
    r = requests.post(
        URL + f"/upload_image/{location_id}", files={"file": open("image.png", "rb")}
    )
    assert r.status_code == 201
    return r.json()

def delete_image(image_id):
    r = requests.delete(URL + f"/image/{image_id}")
    assert r.status_code == 200

def get_image_data(image_id):
    r = requests.get(URL + f"/image/{image_id}/data")
    assert r.status_code == 200
    return r.json()

def create_user():
    user = {
        "username": Faker().user_name(),
        "name": Faker().name(),
        "profile_picture": Faker().image_url(),
    }
    r = requests.post(URL + "/user", json=user)
    assert r.status_code == 201
    return r.json()

def get_user(user_id):
    r = requests.get(URL + f"/user/{user_id}")
    assert r.status_code == 200
    return r.json()

def get_user_visits(user_id):
    r = requests.get(URL + f"/user/{user_id}/visits")
    assert r.status_code == 200
    return r.json()

def create_visit(user_id, location_id):
    r = requests.post(URL + f"/user/{user_id}/visit/{location_id}")
    assert r.status_code == 201
    return r.json()

if __name__ == "__main__":
    print("Checking server is up")
    try:
        ping()
    except Exception as e:
        print("Server is down")
        raise e

    print("Test make location")
    location = make_location()
    location_id = location["id"]
    print(location)

    print("Test get location")
    location = get_location(location_id)

    print("Test get locations")
    locations = get_locations()
    assert location in locations

    print("Test upload image")
    image = upload_image(location_id)
    image_id = image["id"]

    print("Test get image")
    image = get_image(image_id)
    assert image == open("image.png", "rb").read()

    print("Test get image data")
    image_data = get_image_data(image_id)
    assert image_data["location_id"] == location_id

    print("Test get images")
    images = get_images(location_id)
    assert image_data in images

    print("Test get nearby")
    nearby = get_nearby_locations(
        location["geometry"]["coordinates"][0][0][0],
        location["geometry"]["coordinates"][0][0][1],
        1000,
    )
    assert location in nearby

    print("Test create user")
    user = create_user()
    user_id = user["username"]
    assert user == get_user(user_id)

    print("Test create visit")
    visit = create_visit(user_id, location_id)
    assert visit in get_user_visits(user_id)


import requests
from bs4 import BeautifulSoup
from nltk.sentiment.vader import SentimentIntensityAnalyzer  # Import VADER
from flask import Flask, request, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app, resources={r"/search": {"origins": "http://localhost:3000"}}, methods=["POST"])

# Initialize the VADER sentiment analyzer (global variable)
sia = SentimentIntensityAnalyzer()

# Function to perform the search
def perform_search(keyword):
    try:
        # Construct the search URL with the user's keyword
        search_url = f'https://hn.algolia.com/?q={keyword}'
        print("Performing search with keyword:", keyword)
        print("Search URL:", search_url)  # Print the search URL
        response = requests.get(search_url)
        if response.status_code == 200:
            print("Request successful. Parsing HTML... Status Code:", response.status_code)
            # Pass the URL to analyze_brand_comments and capture the results
            search_results = analyze_brand_comments(search_url, keyword)
            if search_results:
                return search_results  # Return the results
        else:
            print(f'Failed to fetch search results. Status code: {response.status_code}')
            return None
    except Exception as e:
        print(f'Error: {e}')
        return None

# Function to scrape comments, analyze sentiment, and classify
def analyze_brand_comments(url, keyword=None):
    try:
        # Send an HTTP request to the target URL
        response = requests.get(url)

        # Check if the request was successful
        if response.status_code == 200:
            print("Request to", url, "successful. Parsing HTML...")

            # Parse the HTML content
            soup = BeautifulSoup(response.content, "html.parser")

            # Extract article titles from the specified class ("Story_title")
            article_titles = [a.text for a in soup.select(".Story_title span")]

            # Process and analyze comments from each article title
            results = []

            for article_title in article_titles:
                title_text = article_title

                # Check if the keyword is in the article title
                if keyword is not None and keyword.lower() in title_text.lower():
                    # Perform sentiment analysis on the article title using sia
                    sentiment_scores = sia.polarity_scores(title_text)

                    # Determine sentiment based on compound score
                    compound_score = sentiment_scores['compound']

                    if compound_score >= 0.05:
                        sentiment = "positive"
                    elif compound_score <= -0.05:
                        sentiment = "negative"
                    else:
                        sentiment = "neutral"

                    # Add the result to the list
                    results.append({"keyword": keyword, "article_title": title_text, "sentiment": sentiment})

            # Return the list of results
            return results

        # Return an empty list when no matching articles are found
        return []
    except Exception as e:
        print(f'Error in analyze_brand_comments: {e}')
        return None

@app.route('/search', methods=['POST'])
def search_endpoint():
    data = request.get_json()
    keyword = data.get('keyword', '')

    print(f"Performing search for keyword: {keyword}")

    # Call the perform_search function to perform the search
    search_results = perform_search(keyword)

    if search_results:
        print("Search successful.")
        return jsonify(search_results)
    else:
        print("Search failed.")
        return jsonify([])

if __name__ == '__main__':
    app.run(debug=True)


def selectionSort(ar):
	"""
	  SELECTION SORT
	  iteratively swap mininum of remaining slice
	  Modifies original array (in-place method)
	"""
	n = len( ar )
	if  n < 2 : return ar
	# i absolute position of first element of slice starting at i: ar[i:]
	i = 0
	while i< n-1 :
		m = min( ar[i:] )
		#finds index of 1st instance of m in slice starting at i
		jm = ar[i:].index(m)
		tmp = ar[i]
		ar[i] = m
		#jm is position relative to slice size. Add i for absolute position in ar
		ar[jm+i]= tmp
		#print( "DEBUG: jm+i="+str(jm+i)+" i="+str(i)+" "+str(ar) )
		i+=1
	#return ar

def badSelectionSort(A):
	"""
	Given the list of #s
	  R1, R2, ..., Ri, Ri+1, ...,Rj, Rj+1, ..., Rn
	consider an arbitrary position i. We'll compare the
	following positions against this one.

	For a pos. j>i, if Rj < Ri, swap values. Increase j; repeat comparison.
	This will put the smallest number between i and n in position i.

	Repeat for all i.

	Thus, this is NOT BUBBLE SORT, but a bad implementation of Selection sort!
	"""
	l = len(A)
	i=0 ; j=0 ; tmp=A[0]
	while i < l:
		j = i+1
		while j<l:
			if A[j] < A[i]:
				tmp = A[i]
				A[i]= A[j]
				A[j]= tmp
			j +=1
		i += 1

def amin_bare(A,i,im):
	n = len(A)
	im[0] = i
	im[1] = A[i]
	k = i+1
	f = False
	while k < n:
		"""
		Avoiding branch prediction overhead
		if A[k] < m :
			m=A[k]
			im = k
		"""
		f = A[k] < im[1]
		im[1] = A[k] * f + im[1] * (1-f)
		im[0] =   k  * f + im[0] * (1-f)
		k += 1
	#return [im,m]
	#print("DEBUG: min A["+str(i)+":]="+str(A[i:])+" : m="+str(im[1])+" im="+str(im[0]) )

def amin(A,i):
	n = len(A)
	im = i
	f =  A[i] <= A[n-1]
	m = f * A[i] + (1-f) * A[n-1]
	for k in range(i+1,n-1):
		if A[k] < m:
			m = A[k]
			im = k
		"""
		This takes 4 times more than the previous if-then!!!
		f =  A[k] < m
		m = f*A[k] + (1-f)*m
		"""
	return [im,m]

def aminrec(A,i):
	if len(A)-i == 1: return [i,A[i]]
	[im,m] = aminrec(A,i+1) # len = 2000 => max recursion exceeded!
	r = (len(A)-i)//2
	#[im1,m1] = aminrec(A,
	if A[i]<m:
		im = i
		m = A[i]

	return [im,m]
def ssort(A):
	n = len(A)
	#im = [-1,-666]
	im = 0
	m = A[im]
	#for i in range(0,n-1):
	i=-1
	while i < n-1:
		i += 1
		[im,m] = amin(A,i)
		tmp = A[i]
		A[i] = m
		A[im] = tmp
		"""
		amin_bare(A,i,im)
		tmp = A[i]
		A[i] = im[1]
		A[im[0]] = tmp
		"""

"""
r = [87, -3, 1, 6, -17, 3, 2, 1,7]
r = r #+r
"""
#Test
import math
from random import random
N=4000 # Check time N=2k -> N=4k => time -> ~ 4*time.
i=0
r=[]
while i< N:
        sc = math.floor(100*random()+0.5)
        r += [ sc * random() ]
        i += 1

"""
print("Sorting "+str(r) )
#print( swapMin(r) )
print( ssort(r) )
print("Original array now: "+str(r) )
"""
#selectionSort(r)
ssort(r)


from fastapi import FastAPI, Body
from sqlalchemy.testing.suite.test_reflection import users

from auth_bearer import JWTBearer
app = FastAPI()
from models import  UserSchema, UserLoginSchema
from auth_handler import signJWT

@app.post("/user/signup", tags=["user"])
async def create_user(user: UserSchema = Body(...)):
    user.append(user)
    return signJWT(user.email)

def check_user(data: UserLoginSchema):
    for user in users:
        if user.email == data.email and user.password == data.password:
            return True
    return False

@app.post("/user/login", tags=["user"])
async def user_login(user: UserLoginSchema = Body(...)):
    if check_user(user):
        return signJWT(user.email)
    return {
        "error": "Wrong login details!"
    }


import requests
from helpers import GenericImages as GI
from api import Champions as Champs
from helpers import UserID as UID

# Queue IDs --------------------------------------------------------------------------------------------

# Solo Ranked         : 420
# Flex Ranked         : 440

# Normal Blind        : 430
# Normal Draft        : 400

# ARAM                : 450

# Co-op [Intro]       : 830
# Co-op [Beginner]    : 840
# Co-op [Intermediate]: 850

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

def getLiveGameInfo(api, region, version, summonerID):
    endpoint = f"https://{region}.api.riotgames.com/lol/spectator/v4/active-games/by-summoner/{summonerID}"
    response = requests.get(endpoint, params={'api_key': api})

    if response.status_code != 200:
        print("The summoner is not currently in a live game.")
        return []

    data = response.json()

    players_info = []

    for player in data['participants']:
        player_data = {}

        player_data['PlayerName'] = player['summonerName']
        player_data['PlayerTeam'] = 'Blue' if player['teamId'] == 100 else 'Red'

        player_data['ChampionID'] = player['championId']
        champList = []
        champList.append(player_data['ChampionID'])
        champInfo = Champs.getChampsInfo(version, champList)
        player_data['ChampionName'] = champInfo[0]['ChampionName']
        player_data['ChampionImage'] = GI.getChampImage(version, player_data['ChampionName'])

        player_data['Keystone'] = GI.getRuneImage(version, player["perks"]["perkIds"][0])
        player_data['SecondaryRune'] = GI.getRuneImage(version, player["perks"]["perkSubStyle"])

        player_data['Summoner1'] = GI.getSummonerSpellImage(version, player['spell1Id'])
        player_data['Summoner2'] = GI.getSummonerSpellImage(version, player['spell2Id'])

        players_info.append(player_data)

    return players_info

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


def countApplesAndOranges(s,t,a,b,apples,oranges):
    '''
    Counts the number of apples and oranges that fall into a
    range [s,t]
    '''
    apple_count = 0
    orange_count = 0

    for i in apples:
        pos =  a + i
        if pos >= s and pos <= t:
            apple_count += 1

    for i in oranges:
        pos = b + i
        if pos >= s and pos <= t:
            orange_count += 1

    print(apple_count)
    print(orange_count)

print('Case:')
print('House position: [7,11], Apple tree: 5,\nOrange tree: 15, Apples: [-2,2,1], Oranges: [5,-6]\n')
print('Results:')
countApplesAndOranges(7,11,5,15,[-2,2,1],[5,-6])

print('\nCase:')
print('House position: [-9,-5], Apple tree: -12,\nOrange tree: -3, Apples: [4,3,-2,10], Oranges: [-3,4,-1]\n')
print('Results:')
countApplesAndOranges(-9,-5,-12,-3,[4,3,-2,10],[-3,4,1])


import boto3

class SQS(object):
    def __init__(self, queue_name):
        self.client = boto3.client('sqs')
        self.queue_name = queue_name
        self.queue_url = self.get_queue_url()

    def get_queue_url(self):
        response = self.client.get_queue_url(
            QueueName=self.queue_name,
        )
        return response['QueueUrl']

    def send_message(self, body):
        response = self.client.send_message(
            QueueUrl=self.queue_url,
            MessageBody=body
        )
        return response

    def send_fifo_message(self, body, group_id='1'):
        response = self.client.send_message(
            QueueUrl=self.queue_url,
            MessageBody=body,
            MessageGroupId=group_id,
        )
        return response

    def receive_messages(self, number_of_messages=10):
        response = self.client.receive_message(
            QueueUrl=self.queue_url,
            MaxNumberOfMessages=number_of_messages
        )
        return response.get('Messages')

    def delete_message(self, receipt_handle):
        response = self.client.delete_message(
            QueueUrl=self.queue_url,
            ReceiptHandle=receipt_handle
        )
        return response