"""Custom TorchServe model handler for SAM encoder model.
"""
from ts.torch_handler.base_handler import BaseHandler
import numpy as np
import base64
import torch
from PIL import Image
from io import BytesIO
from typing import Union
import os
from time import time
from segment_anything.utils.transforms import ResizeLongestSide
import onnxruntime
import rasterio
from pyproj import CRS, Transformer
from shapely import ops
from shapely.geometry import mapping, shape, MultiPolygon
import json
from rasterio import features
# import ptvsd

# ptvsd.enable_attach(address=('0.0.0.0', 6789), redirect_output=True)
# ptvsd.wait_for_attach()
np.random.seed(42)
os.environ["PYTHONHASHSEED"] = "42"

class ModelHandler(BaseHandler):
    """
    A custom model handler implementation.
    """

    def __init__(self):
        # call superclass initializer
        super().__init__()

    def preprocess(self, data):
        """Converts input images to float tensors.
        Args:
            data (List): Input data from the request in the form of a list of image tensors.
        Returns:
            Tensor: single Tensor of shape [BATCH_SIZE=1, 3, IMG_SIZE, IMG_SIZE]
        """
        start = time()
        row = data[0]
        self.payload = row.get("data") or row.get("body")
        if isinstance(self.payload, dict) and "image_embeddings" in self.payload \
            and "image_shape" in self.payload \
            and "input_point" in self.payload \
            and "input_label" in self.payload:
            image_embeddings = self.payload['image_embeddings']
        else:
            print("one of image_shape, input_point, input_label, image_embeddings payload dict keys missing.")
            assert False
        if isinstance(image_embeddings, str):
            image_embeddings = base64.b64decode(image_embeddings)
        image_embeddings = np.frombuffer(image_embeddings, dtype=np.float32)
        image_embeddings = image_embeddings.reshape((1,256,64,64))
        print("XXXXX  Preprocess time: ", time()-start)
        return image_embeddings

    def initialize(self, context):
        """
        Invoke by torchserve for loading a model
        :param context: context contains model server system properties
        :return:
        """
        start = time()
        properties = context.system_properties
        model_dir = properties.get("model_dir")
        # gpu_id is None if number_of_gpu=0 set in config.properties
        self.device = torch.device("cuda:" + str(properties.get("gpu_id")) if torch.cuda.is_available()  and str(properties.get("gpu_id")) != "None" else "cpu")
        serialized_file = context.manifest['model']['serializedFile']
        onnx_model_path = os.path.join(model_dir, serialized_file)
        self.ort_session = onnxruntime.InferenceSession(onnx_model_path)
        self.initialized = True
        print("XXXXX  Initialization time: ", time()-start)

    def inference_single_point(self, image_embeddings):
        """
        Internal inference methods
        :param model_input: transformed model input data
        :return: list of inference output in NDArray
        """
        start = time()
        resizer = ResizeLongestSide(1024) # 1024 is the max for the export onnx example nb
        onnx_coord = np.concatenate([np.array(self.payload['input_point'])[np.newaxis,:], np.array([[0.0, 0.0]])], axis=0)[None, :, :]
        onnx_label = np.concatenate([np.array([self.payload['input_label']]), np.array([-1])], axis=0)[None, :].astype(np.float32)
        onnx_coord = resizer.apply_coords(onnx_coord, self.payload.get("image_shape")).astype(np.float32)
        onnx_mask_input = np.zeros((1, 1, 256, 256), dtype=np.float32)
        onnx_has_mask_input = np.zeros(1, dtype=np.float32)
        ort_inputs = {
            "image_embeddings": image_embeddings,
            "point_coords": onnx_coord,
            "point_labels": onnx_label,
            "mask_input": onnx_mask_input,
            "has_mask_input": onnx_has_mask_input,
            "orig_im_size": np.array(self.payload.get("image_shape"), dtype=np.float32)
        }
        masks, scores, low_res_logits = self.ort_session.run(None, ort_inputs)
        masks = masks > .5 # TODO make this configurable
        print("XXXXX  Inference time: ", time()-start)
        print("XXXXXXX ambiguous mask proposals shape for single point", masks.shape) #(512,512) for single point mask
        return masks[0], scores[0]

    def mask_to_geojson(self, mask, scores):
        transform = rasterio.transform.from_bounds(*self.payload.get("bbox"), mask.shape[1], mask.shape[0])
        # A list to store all features
        all_polygons = []

        # Generate shapes from each mask
        for geoshape, value in features.shapes(mask.astype(np.uint8), mask=mask, transform=transform):
            # Convert each shape to GeoJSON
            polygon = shape(geoshape)
            all_polygons.append(polygon)

        # Merge all polygons into a single MultiPolygon
        multi_polygon = MultiPolygon(all_polygons)
        # Define the source CRS
        crs_source = CRS(self.payload.get("crs"))
        # Define the target CRS
        crs_target = CRS("EPSG:4326")
        # Initialize the transformer
        transformer = Transformer.from_crs(crs_source, crs_target, always_xy=True)
        # Reproject all polygons
        multipolygon_reprojected = ops.transform(transformer.transform, multi_polygon)
        multi_polygon_geojson = mapping(multipolygon_reprojected)
        multi_polygon_geojson['properties'] = {"class_label": self.payload.get('input_label'),
                                               "confidence_scores": [np_to_py_type(score) for score in scores]}
        return json.dumps(multi_polygon_geojson)

    def postprocess(self, masks):
        if isinstance(masks, np.ndarray):
            # it's not georeferenced, we encode the np array. otherwise it's a geojson
            masks = base64.b64encode(masks.flatten()).decode("utf-8")
        return masks

    def handle(self, data, context):
        """
        Invoke by TorchServe for prediction request.
        Do pre-processing of data, prediction using model and postprocessing of prediciton output
        :param data: Input data for prediction
        :param context: Initial context contains model server system properties.
        :return: prediction output
        """
        model_input = self.preprocess(data)
        # (N,512,512) mask input for single point, ambiguous proposals, highest conf not always best
        masks, scores = self.inference_single_point(model_input)
        if self.payload.get("crs") is not None and self.payload.get("bbox") is not None:
            geojsons = []
            for mask in masks:# need to clean this up and apply conversion to each ambiguous mask
                print(f"xxxxxxxxxShape mask: {mask.shape}")
                multipolygon = self.mask_to_geojson(mask, scores)
                geojsons.append(multipolygon)
            return [{"status": "success", "geojsons": geojsons, "confidence_scores": [np_to_py_type(score) for score in scores]}]
        else:
            masks = self.postprocess(masks)
            return [{"status": "success", "masks": masks, "confidence_scores": [np_to_py_type(score) for score in scores]}]

def np_to_py_type(o):
    if isinstance(o, np.generic):
        return o.item()
    raise TypeError

def open_image(input_file: Union[str, BytesIO]) -> Image:
    """
    Opens an image in binary format using PIL.Image and converts to RGB mode.

    Supports local files or URLs.
    This operation is lazy; image will not be actually loaded until the first
    operation that needs to load it (for example, resizing), so file opening
    errors can show up later.
    Args:
        input_file: str or BytesIO, either a path to an image file (anything
            that PIL can open), or an image as a stream of bytes
    Returns:
        an PIL image object in RGB mode
    """
    n_retries = 10
    retry_sleep_time = 0.01
    error_names_for_retry = ['ConnectionError']
    if (isinstance(input_file, str)
            and input_file.startswith(('http://', 'https://'))):
        try:
            response = requests.get(input_file)
        except Exception as e:
            print(f'Error retrieving image {input_file}: {e}')
            success = False
            if e.__class__.__name__ in error_names_for_retry:
                for i_retry in range(0,n_retries):
                    try:
                        time.sleep(retry_sleep_time)
                        response = requests.get(input_file)
                    except Exception as e:
                        print(f'Error retrieving image {input_file} on retry {i_retry}: {e}')
                        continue
                    print('Succeeded on retry {}'.format(i_retry))
                    success = True
                    break
            if not success:
                raise
        try:
            image = Image.open(BytesIO(response.content))
        except Exception as e:
            print(f'Error opening image {input_file}: {e}')
            raise

    else:
        print("trying to open image")
        image = Image.open(input_file)
    if image.mode not in ('RGBA', 'RGB', 'L', 'I;16'):
        raise AttributeError(
            f'Image {input_file} uses unsupported mode {image.mode}')
    if image.mode == 'RGBA' or image.mode == 'L':
        print("trying to convert image")
        # PIL.Image.convert() returns a converted copy of this image
        image = image.convert(mode='RGB')

    # Alter orientation as needed according to EXIF tag 0x112 (274) for Orientation
    #
    # https://gist.github.com/dangtrinhnt/a577ece4cbe5364aad28
    # https://www.media.mit.edu/pia/Research/deepview/exif.html
    #
    try:
        exif = image._getexif()
        orientation: int = exif.get(274, None)  # 274 is the key for the Orientation field
        if orientation is not None and orientation in IMAGE_ROTATIONS:
            image = image.rotate(IMAGE_ROTATIONS[orientation], expand=True)  # returns a rotated copy
    except Exception:
        pass

    return image

def load_image(input_file: Union[str, BytesIO]) -> Image:
    """
    Loads the image at input_file as a PIL Image into memory.
    Image.open() used in open_image() is lazy and errors will occur downstream
    if not explicitly loaded.
    Args:
        input_file: str or BytesIO, either a path to an image file (anything
            that PIL can open), or an image as a stream of bytes
    Returns: PIL.Image.Image, in RGB mode
    """
    image = open_image(input_file)
    image.load()
    return image


class Solution:
    def thirdMax(self, nums: List[int]) -> int:
        unique = list(set(nums))
        #print(unique)
        unique.sort(reverse=True)
        if len(unique) < 3:
            return unique[0]
        else:
            return unique[2]


"""
ERC20 token.
"""
from collections import defaultdict
from typing import Dict, Any, List

from aave_tokens_model.core.logging import Logged
from aave_tokens_model.core.utilities import (
    AddressT, require, generate_address
)
from aave_tokens_model.core.utilities.restriction import NOT_ENOUGH_BALANCE

class ERC20(Logged):
    """
    The most common ERC20 implementation.
    """

    def __init__(self, name: str, symbol: str, verbose: bool = False) -> None:
        """Prepare new token."""
        super().__init__(verbose)
        self._name = name
        self._symbol = symbol
        self._verbose = verbose
        self._address = generate_address()

        self._balances: Dict[AddressT, float] = defaultdict(lambda: 0)
        self._total_supply: float = 0

    def _get_context(self, function: str, stage: str) -> Dict[str, Any]:
        context = super()._get_context(function, stage)
        context['symbol'] = self._symbol
        return context

    def _prepare_log_before(self, action, *args, **kwargs) -> List[str]:
        base_msg = super()._prepare_log_before(action, *args, **kwargs)
        base_msg.append(base_msg[-1])
        base_msg[-2] = f'total supply = {self._total_supply}'
        return base_msg

    def _prepare_log_after(self, action, *args, **kwargs) -> List[str]:
        base_msg = super()._prepare_log_after(action, *args, **kwargs)
        base_msg.append(base_msg[-1])
        base_msg[-2] = f'new total supply = {self._total_supply}'
        return base_msg

    @staticmethod
    def _format_log_msg(record) -> str:
        msg = Logged._format_log_msg(record)
        extra = record.get('extra', {})
        token_symbol = extra.get('symbol', 'unknown-token')
        start = msg.find('>')
        return msg[:start + 1] + f'{token_symbol}:' + msg[start + 1:]

    @property
    def name(self) -> str:
        """Get name of token."""
        return self._name

    @property
    def symbol(self) -> str:
        """Get symbol of token."""
        return self._symbol

    @property
    def address(self) -> AddressT:
        """Get address of token."""
        return self._address

    def total_supply(self) -> float:
        """Get total amount of minted tokens."""
        return self._total_supply

    def balance_of(self, user: AddressT) -> float:
        """Get amount of tokens held by the specific user."""
        return self._balances[user]

    @Logged.with_log
    def transfer(self, user: AddressT, to: AddressT, value: float) -> bool:
        """
        Transfer an amount from user to the specific address.

        Return an indicator of transfer success.
        """
        require(self._balances[user] >= value, NOT_ENOUGH_BALANCE)
        self._balances[user] -= value
        self._balances[to] += value

        return True

    @Logged.with_log
    def mint(self, user: AddressT, value: float) -> float:
        """Mint new tokens for user; return new balance."""
        self._balances[user] += value
        self._total_supply += value
        return self._balances[user]

    @Logged.with_log
    def burn(self, user: AddressT, value: float) -> float:
        """Burn tokens for user; return new balance."""
        require(self._balances[user] >= value, NOT_ENOUGH_BALANCE)
        self._balances[user] -= value
        self._total_supply -= value
        return self._balances[user]


# coding=utf-8
import datetime
import logging
import os
import traceback

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# 日志
from qichacha1.enterpriseEntity import Enterprise

def get_logger(name):
    log = logging.getLogger(name)
    log.setLevel(logging.DEBUG)

    # Standard output handler
    sh = logging.StreamHandler()
    sh.setLevel(logging.DEBUG)
    sh.setFormatter(logging.Formatter('%(levelname)s - %(name)s:%(lineno)s: %(message)s'))
    log.addHandler(sh)
    return log

logger = get_logger(__file__)

# 测试用例执行函数
def work(browser):
    global company
    url = 'https://www.qichacha.com/search?key='
    # 读取全部企业
    companies = getCompanies()
    browser.get('https://www.qichacha.com')

    cookie = browser.get_cookie("PHPSESSID")
    del cookie['value']
    cookie['value'] = "oqcght5edqgs8s027bvooloo86"
    browser.add_cookie(cookie)

    out = open('E:/outInfo.txt', 'a')
    try:
        for company in companies:
            infoUrl = url + company
            browser.get(infoUrl)
            html1 = browser.execute_script("return document.documentElement.outerHTML")
            soup = BeautifulSoup(html1, "lxml")
            trs = soup.find("div", {"id": "ajaxlist"}).find("section").find("table").find("tbody").findAll("tr")
            tds = trs[0].findAll("td")
            ps = tds[1].findAll("p")
            legal = ps[0].find("a").string.replace("\n", "").strip()
            enterpriseName = tds[1].find("a").getText().replace("\n", "").strip()
            registerCapital = ps[0].findAll("span")[0].string.replace("注册资本：", "").replace("\n", "").strip()
            buildTime = ps[0].findAll("span")[1].string.replace("成立时间：", "").replace("\n", "").strip()
            emailAndPhone = ps[1].getText().replace("\n", "").strip()
            email = emailAndPhone[emailAndPhone.find("邮箱："):emailAndPhone.find("电话：")].replace("邮箱：", "").strip()
            phone = ps[1].find("span").string.replace("电话：", "").replace("\n", "").strip()
            address = ps[2].getText().replace("地址：", "").replace("\n", "").strip()
            enterprise = Enterprise(enterpriseName, legal, registerCapital, buildTime, email, phone, address)
            out.write(getCvs(enterprise) + '\n')
            # sleep(1)
            out.flush()
    except Exception as e:
        logger.error("{} can not get info", company)
        print(company + " can not get info")
    finally:
        out.close()

def writeLog():
    # 组合日志文件名（当前文件名+当前时间）.比如：case_login_success_20150817192533
    basename = os.path.splitext(os.path.basename(__file__))[0]
    logFile = basename + "-" + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + ".log"
    logging.basicConfig(filename=logFile)
    s = traceback.format_exc()
    logging.error(s)
    browser.get_screenshot_as_file("./" + logFile + "-screenshot_error.png")

def getCompanies():
    companies = [];
    f = open("E:\enterprise.txt", encoding='utf-8')
    # 返回一个文件对象
    line = f.readline()  # 调用文件的 readline()方法
    while line:
        # print(line, end = '')　　　# 在 Python 3中使用
        companies.append(line.replace("\n", ""))
        line = f.readline()
    f.close()
    return companies

# 导出
def getCvs(enterprise):
    outCvs = '"' + enterprise.name + '","' + enterprise.legal + '","' + enterprise.registerCapital + '","' + enterprise.buildTime + '","' + enterprise.email + '","' + enterprise.phone + '","' + enterprise.address + '"'
    print(outCvs)
    return outCvs

if __name__ == "__main__":
    chrome_options = Options()
    # chrome_options.add_argument('--headless')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--no-sandbox')
    browser = webdriver.Chrome(chrome_options=chrome_options)  # Chrome界面
    # browser = webdriver.PhantomJS()  # 无界面
    work(browser)
    browser.quit()


from client import Client
import globalContent
class Employee:
    def __init__(self):
        self.first_name = ""
        self.last_name = ""
        self.id = 1
    def __init__(self, **kwargs):
        self.first_name = kwargs['first_name']
        self.last_name = kwargs['last_name']
        self.id = kwargs['id']

    def __str__(self):
        return "First Name: {}, Last Name: {}, ID: {}".format(
            self.first_name, self.last_name, self.id
        )

    def createClient(self, first_name, last_name, cpf, email,phone):
        client = Client(first_name, last_name, cpf, email,phone)
        globalContent.database.clientContainer.append(client)

    def searchClient(self, cpf):
        for i in globalContent.database.clientContainer:
            if i.cpf == cpf:
                return i

    def deleteClient(self, cpf):
        for i in globalContent.database.clientContainer:
            if i.cpf == cpf:
                globalContent.database.clientContainer.remove(i)

                return True

        return False

    def updateClient(self, client,
        first_name = "", last_name = "",
         cpf = "", email = "", phone = ""):
        for i in globalContent.database.clientContainer:
            if i == client:
                if(first_name == ""): i.first_name = i.first_name
                else:
                    i.first_name = first_name
                if(last_name == ""): i.last_name = i.last_name
                else:
                    i.last_name = last_name
                if(cpf == ""): i.cpf = i.cpf
                else:
                    i.cpf = cpf
                if(email == ""): i.email = i.email
                else:
                    i.email = email
                if(phone == ""): i.phone = i.phone
                else:
                    i.phone = phone

    def listClients(self):
        print("\n----------Lista de clientes-------")
        idx = 1
        for i in globalContent.database.clientContainer:
            print('('+str(idx)+')' + str(i))
            idx = idx + 1

    def rent(self, item, client):
        if item.status == 'rented':
            #print('item já está alugado.')
            return False
        else:
            item.status = 'rented'
            #print("item alugado com sucesso")
            client.rentedItems.append(item) #status alugado
            return True

    def giveBack(self, client):
        if len(client.rentedItems) > 0:
            for i in client.rentedItems:
                i.status = 'available'
                client.rentedItems.remove(i)
                print(i.item_name + " devolvido")
        else:
            print("cliente não possui itens emprestados")

    def updateItem(self, item, item_name="", id_item=-1,
        value=-1, description="", status = ""
    ):

        for i in globalContent.database.itemsContainer:
            if i == item:

                if item_name == "": i.item_name = i.item_name
                else:
                    i.item_name = item_name
                if id_item == -1:
                    i.id_item = i.id_item
                else:
                    i.id_item = id_item
                if value == -1:
                    i.value = i.value
                else:
                    i.value = value
                if description == "":
                    i.description = i.description
                else:
                    i.description = description
                if status == "":
                    i.status = i.status
                else:
                    i.status = status

    def searchItem(self, id_item):
        for i in globalContent.database.itemsContainer:
            if i.id_item == id_item:
                return i

    def listItems(self):
        print('\n----- Lista de Itens -----\n')
        idx = 1
        for i in globalContent.database.itemsContainer:
            print('('+str(idx)+')' + str(i))
            idx = idx + 1

    def showMenuClients(self):
        while (True):
            option = input('\n----- MENU ITENS ----- \n'
                           + '(1) - Listar Clientes\n'
                           + '(2) - Adicionar Cliente\n'
                           + '(3) - Editar Cliente\n'
                           + '(4) - Remover Cliente\n'
                           + '(5) - SAIR\n')

            if (option == '1'):
                self.listClients()
            elif (option == '2'):
                name = input('digite o nome\n')
                lastName = input('ultimo nome\n')
                cpf = input('digite o cpf\n')
                phone = input('digite o telefone\n')
                email = input('digite o email\n')
                self.createClient(name, lastName, cpf, email, phone)
                self.listClients()
            elif (option == '3'):
                searchcpf = input('digite o cpf\n')
                client = self.searchClient(searchcpf)

                if (client):
                    print("\n --- dados para editar: \n")
                    name = input('digite o nome\n')
                    lastName = input('ultimo nome\n')
                    cpf = input('digite o cpf\n')
                    phone = input('digite o telefone\n')
                    email = input('digite o email\n')

                    self.updateClient(client=client, first_name=name,
                                      last_name=lastName, cpf=cpf,
                                      phone=phone,
                                      email=email)
                    self.listClients()
                else:
                    print("erro")
            elif (option == '4'):
                cpf = input('digite o cpf\n')
                deleted = self.deleteClient(cpf)
                if deleted:
                    self.listClients()
                else:
                    print('erro ao remover cliente')
            else:
                break

    def showMenuItems(self):
        while(True):
            option = input('\n----- MENU ITENS ----- \n'
                            + '(1) - Listar Itens\n'
                            + '(2) - Alugar Item\n'
                            + '(3) - Devolver Item\n'
                            + '(4) - Atualizar Item\n'
                            + '(5) - SAIR\n')

            if(option == '1'): self.listItems()
            elif(option == '2'):
                self.listItems()
                item = input('\nItem que será alugado: ')
                self.listClients()
                client = input('\nCliente a alugar: ')
                self.rent(globalContent.database.itemsContainer[int(item) - 1], globalContent.database.clientContainer[int(client) - 1])
            elif(option == '3'):
                cpf = input('\n digite o cpf do cliente que vai devolver: \n')
                client = self.searchClient(cpf)
                self.giveBack(client)
                self.listClients()
            elif(option == '4'):
                iditem = int(input('identificador do item: \n'))
                item = self.searchItem(iditem)
                print(item)
                name = input('digite o novo nome:\n')
                value = input('digite o novo valor: \n')
                desc = input('digite a nova descrição\n')

                self.updateItem(item=item, item_name=name, value=value, description=desc)
                self.listItems()
            else:
                break


from logging import raiseExceptions
import numpy as np
from gym.utils import seeding
import gym
from gym import spaces
import pandas as pd
import argparse

parser = argparse.ArgumentParser()
# notice that although the title contains OE, the state does not contain any information about order
# but traditional OHLCV like algorithm trading
# the key difference of this OE and algorithm trading is that it can only put action at one side, i.e. if
#the OE task is asked to buy one share of BTC, you can not put ask order even you still have BTC in your hand
# therefore, we use the dataset of BTC algorithm trading yet the env is a little different.
# in oe, we want to sell at the highest or buy at the lowest, therefore the optimization target is set to be
# the amount of money we sell, if the target is buying, our target will be nagative, they will both be optimized
# to their max
# all of the trades will be conducted at their close price
# this give me a little bit confusion about the sparation way of dataset, because it is not like the
#discrete action space where we can adjust the target amount with portion to the that of a train dataset
# and the valid dataset, which might cause a problem. Here to be more speicifcally
parser.add_argument(
    "--df_path",
    type=str,
    default="data/data/BTC/valid.csv",
    help="the path for the downloaded data to generate the environment")
parser.add_argument("--tech_indicator_list",
                    type=list,
                    default=[
                        "high", "low", "open", "close", "adjcp", "zopen",
                        "zhigh", "zlow", "zadjcp", "zclose", "zd_5", "zd_10",
                        "zd_15", "zd_20", "zd_25", "zd_30"
                    ],
                    help="the name of the features to predict the label")
parser.add_argument("--sell_target",
                    type=int,
                    default=1,
                    choices=[-1, 1],
                    help="1 for sell and -1 for buy")
parser.add_argument("--num_back_day",
                    type=int,
                    default=10,
                    help="number of backwards day")

class TradingEnv(gym.Env):
    def __init__(self, config):
        #init
        self.tech_indicator_list = config["tech_indicator_list"]
        self.df = pd.read_csv(config["df_path"], index_col=0)
        self.action_target = config["sell_target"]
        self.backward_num_day = config["num_back_day"]
        self.action_space = spaces.Box(
            low=0,
            high=1,
            shape=(1, ),
        )
        self.observation_space = spaces.Box(
            low=-np.inf,
            high=np.inf,
            shape=(self.backward_num_day, len(self.tech_indicator_list)),
        )
        # reset

        # the compound_memory's element consists of 2 parts: the cash and the number of bitcoin you have in hand

        self.day = self.backward_num_day
        self.data_public_imperfect = self.df.iloc[
            self.day - self.backward_num_day:self.day, :]
        self.data_public_perfect = self.df.iloc[
            self.day - self.backward_num_day:self.day +
            self.backward_num_day, :]
        self.public_imperfect_state = [
            self.data_public_imperfect[
                self.tech_indicator_list].values.tolist()
        ]
        self.public_perfect_state = [
            self.data_public_perfect[self.tech_indicator_list].values.tolist()
        ]
        # private state indicates the left date and order
        self.private_state = np.array([1, 1])
        self.terminal = False
        self.money_sold = 0
        self.private_state_list = [self.private_state] * self.backward_num_day

    def reset(self):
        self.terminal = False
        self.day = self.backward_num_day
        self.data_public_imperfect = self.df.iloc[
            self.day - self.backward_num_day:self.day, :]
        self.data_public_perfect = self.df.iloc[
            self.day - self.backward_num_day:self.day +
            self.backward_num_day, :]
        self.public_imperfect_state = [
            self.data_public_imperfect[
                self.tech_indicator_list].values.tolist()
        ]
        self.public_perfect_state = [
            self.data_public_perfect[self.tech_indicator_list].values.tolist()
        ]
        # private state indicates the
        self.private_state = np.array([1, 1])
        self.money_sold = 0
        self.private_state_list = [self.private_state] * self.backward_num_day
        return np.array(self.public_imperfect_state), {
            "perfect_state": np.array(self.public_perfect_state),
            "private_state": np.array([self.private_state_list])
        }

    def step(self, action):
        # based on the current price information, we decided whether to trade use the next day's price
        # the reward is calculated as at*(p_(t+1)-average(p))
        self.day = self.day + 1

        self.terminal = (self.day >= (len(self.df) - self.backward_num_day))
        if self.terminal:
            leftover_day, leftover_order = self.private_state
            print("done")
            self.data_public_imperfect = self.df.iloc[
                self.day - self.backward_num_day:self.day, :]
            current_price = self.data_public_imperfect.iloc[-1].close
            self.money_sold += leftover_order * current_price
            self.public_imperfect_state = np.array(self.public_imperfect_state)
            self.private_state_list.append([0, 0])
            self.private_state_list.remove(self.private_state_list[0])

            return self.public_imperfect_state, self.reward, self.terminal, {
                "perfect_state": np.array([self.public_perfect_state]),
                "private_state": np.array([self.private_state_list])
            }

        else:
            leftover_day, leftover_order = self.private_state

            previous_average_price = np.mean(self.df.iloc[:self.day -
                                                          1].close.values)
            self.data_public_imperfect = self.df.iloc[
                self.day - self.backward_num_day:self.day, :]
            self.data_public_perfect = self.df.iloc[
                self.day - self.backward_num_day:self.day +
                self.backward_num_day, :]
            self.public_imperfect_state = self.data_public_imperfect[
                self.tech_indicator_list].values.tolist()

            self.public_perfect_state = self.data_public_perfect[
                self.tech_indicator_list].values.tolist()

            self.public_imperfect_state = np.array(
                [self.public_imperfect_state])
            current_price = self.data_public_imperfect.iloc[-1].close
            if np.abs(action) < np.abs(leftover_order):
                self.money_sold += action * current_price
                self.reward = action * (
                    current_price / previous_average_price - 1)
            else:
                self.money_sold += leftover_order * current_price
                self.terminal = True
                print("done")
            leftover_day, leftover_order = leftover_day - 1 / (len(
                self.df) - 2 * self.backward_num_day), leftover_order - action
            self.private_state = np.array([leftover_day, leftover_order])
            self.private_state_list.append(self.private_state)
            self.private_state_list.remove(self.private_state_list[0])
            return self.public_imperfect_state, self.reward, self.terminal, {
                "perfect_state": np.array([self.public_perfect_state]),
                "private_state": np.array([self.private_state_list])
            }

    def find_money_sold(self):
        return self.money_sold

if __name__ == "__main__":
    args = parser.parse_args()
    config = vars(args)
    env = TradingEnv(config)
    print(env.observation_space.shape)
    _, info = env.reset()
    print(info["private_state"].shape)
    # action = 0.00001
    # done = False
    # i = 0
    # while not done:
    #     s, r, done, info = env.step(action)
    #     print(s.shape)
    #     print(info["private_state"].shape)
    # print(env.money_sold)
    # print(i)
    # import yaml
    # # args = parser.parse_args()

    # def save_dict_to_yaml(dict_value: dict, save_path: str):
    #     with open(save_path, 'w') as file:
    #         file.write(yaml.dump(dict_value, allow_unicode=True))

    # def read_yaml_to_dict(yaml_path: str, ):
    #     with open(yaml_path) as file:
    #         dict_value = yaml.load(file.read(), Loader=yaml.FullLoader)
    #         return dict_value

    # save_dict_to_yaml(
    #     vars(args),
    #     "/home/sunshuo/qml/TradeMaster-1/config/input_config/env/OE/OE_for_PD/valid.yml"
    # )


import numpy as np
import configuration as cnf
from src import io_manager, patient, features_extractor, viewer

io_manager = io_manager.IOManager()
patient = patient.Patient()
features_extractor = features_extractor.FeaturesExtractor(patient)
viewer = viewer.Viewer()

patient.load_patient_data(io_manager)
features_extractor.extract_intensities(0)
viewer.show_histogram_from_image(patient.data[:, :, 0], 100)

viewer.show_image(patient.data[:, :, 0], np.ma.masked_outside(patient.data[:, :, 0], 750, 1250))


import os
import os.path as osp

import cv2
import numpy as np
import vispy
from transforms3d.axangles import axangle2mat
from transforms3d.quaternions import quat2mat
from vispy import app, gloo

# import torch
# import copy
import OpenGL.GL as gl
from lib.render_vispy.frustum import Camera3D
from lib.render_vispy.model3d import Model3D, load_models  # noqa

os.environ["PYOPENGL_PLATFORM"] = "egl"

cur_dir = osp.dirname(osp.abspath(__file__))

# app backends: glfw, pyglet, egl
# gl backends: gl2, pyopengl2, gl+
app_backend = "egl"
gl_backend = "gl2"  # "pyopengl2"  # speed: 'gl+' < 'gl2' < 'pyopengl2'
vispy.use(app=app_backend, gl=gl_backend)
print("vispy uses app: {}, gl: {}".format(app_backend, gl_backend))

def shader_from_path(shader_filename):
    shader_path = osp.join(cur_dir, "./shader", shader_filename)
    assert osp.exists(shader_path)
    with open(shader_path, "r") as f:
        return f.read()

def singleton(cls):
    instances = {}

    def get_instance(size, cam, model_paths=None, scale_to_meter=1.0, gpu_id=None):
        if cls not in instances:
            instances[cls] = cls(size, cam, model_paths, scale_to_meter, gpu_id)
        return instances[cls]

    return get_instance

@singleton  # Don't throw GL context into trash when having more than one Renderer instance
class Renderer(app.Canvas):
    """
    NOTE: internally convert RGB to BGR
    """

    def __init__(self, size, cam, model_paths=None, scale_to_meter=1.0, gpu_id=None):
        """
        size: (width, height)
        """
        app.Canvas.__init__(self, show=False, size=size)
        width, height = size
        self.height = height
        self.width = width
        self.shape = (height, width)  # height, width

        # OpenGL is right-hand with (x+ right, y+ up and z- is forward)
        # OpenCV is right-hand with (x+ right, y- up and z+ is forward)
        # We define everything in OUR left-hand global system (x+ is right, y+ is up, z+ is forward)
        # We therefore must flip Y for OpenCV and Z for OpenGL for every operation
        self.opengl_zdir_neg = np.eye(4, dtype=np.float32)
        # self.opengl_zdir_neg[2, 2] = -1
        self.opengl_zdir_neg[1, 1], self.opengl_zdir_neg[2, 2] = -1, -1

        self.set_cam(cam)
        self.setup_views()

        # Set up shader programs
        # fmt: off
        _vertex_code_pointcloud = shader_from_path("point_cloud.vs")
        _fragment_code_pointcloud = shader_from_path("point_cloud.frag")  # varying

        _vertex_code_colored = shader_from_path("colored.vs")
        _fragment_code_colored = shader_from_path("colored.frag")

        # use colored vertex shader
        _fragment_code_bbox = shader_from_path("bbox.frag")

        _vertex_code_textured = shader_from_path("textured.vs")
        _fragment_code_textured = shader_from_path("textured.frag")

        _vertex_code_background = shader_from_path("background.vs")
        _fragment_code_background = shader_from_path("background.frag")
        self.program_pcl = gloo.Program(_vertex_code_pointcloud, _fragment_code_pointcloud)
        self.program_col = gloo.Program(_vertex_code_colored, _fragment_code_colored)
        self.program_bbox = gloo.Program(_vertex_code_colored, _fragment_code_bbox)
        self.program_tex = gloo.Program(_vertex_code_textured, _fragment_code_textured)
        self.program_bg = gloo.Program(_vertex_code_background, _fragment_code_background)
        # fmt: on

        # Texture where we render the color/depth and its FBO
        self.col_tex = gloo.Texture2D(shape=self.shape + (3,))
        self.fbo = gloo.FrameBuffer(self.col_tex, gloo.RenderBuffer(self.shape))
        self.fbo.activate()
        # gloo.set_state(depth_test=True, blend=False, cull_face=True)
        gloo.set_state(depth_test=True, blend=False, cull_face=False)
        gl.glEnable(gl.GL_LINE_SMOOTH)
        # gl.glDisable(gl.GL_LINE_SMOOTH)
        gloo.set_clear_color((0.0, 0.0, 0.0))
        gloo.set_viewport(0, 0, *self.size)

        # Set up background render quad in NDC
        quad = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
        tex = [[0, 1], [1, 1], [1, 0], [0, 0]]
        vertices_type = [
            ("a_position", np.float32, 2),
            ("a_texcoord", np.float32, 2),
        ]
        collated = np.asarray(list(zip(quad, tex)), vertices_type)
        self.bg_vbuffer = gloo.VertexBuffer(collated)
        self.bg_ibuffer = gloo.IndexBuffer([0, 1, 2, 0, 2, 3])

        self.models = None
        if model_paths is not None:
            self._load_models(model_paths, scale_to_meter=scale_to_meter)

    def _load_models(self, model_paths, scale_to_meter=1.0):
        self.models = load_models(model_paths, scale_to_meter=scale_to_meter)

    def set_cam(self, cam, clip_near=0.1, clip_far=100.0):
        self.cam = cam
        self.clip_near = clip_near
        self.clip_far = clip_far
        self.mat_proj = self.projective_matrix(cam, 0, 0, self.shape[1], self.shape[0], clip_near, clip_far)

    def clear(self, color=True, depth=True):
        gloo.clear(color=color, depth=depth)

    def setup_views(self):
        self.view = dict()

        self.view["back"] = np.eye(4)
        self.view["back"][:3, :3] = axangle2mat(axis=[1, 0, 0], angle=15 * np.pi / 180)
        self.view["back"][:3, 3] = [0, -2.0, -3.25]

        self.view["center"] = np.eye(4)

        self.view["front"] = np.eye(4)
        self.view["front"][:3, :3] = axangle2mat(axis=[1, 0, 0], angle=9 * np.pi / 180)
        self.view["front"][:3, 3] = [0, 0, 3.25]

        self.view["show"] = np.eye(4)
        self.view["show"][:3, :3] = axangle2mat(axis=[1, 0, 0], angle=5 * np.pi / 180) @ axangle2mat(
            axis=[0, 1, 0], angle=-15 * np.pi / 180
        )
        self.view["show"][:3, 3] = [-3.5, -1, -5]
        self.used_view = "center"

    def finish(self, only_color=False, to_255=False):
        # NOTE: the colors in Model3D were converted into BGR, so the rgb loaded here is BGR
        im = gl.glReadPixels(0, 0, self.size[0], self.size[1], gl.GL_RGB, gl.GL_FLOAT)
        # Read buffer and flip X
        rgb = np.copy(np.frombuffer(im, np.float32)).reshape(self.shape + (3,))[::-1, :]
        if to_255:
            rgb = (rgb * 255 + 0.5).astype(np.uint8)
        if only_color:
            return rgb

        im = gl.glReadPixels(
            0,
            0,
            self.size[0],
            self.size[1],
            gl.GL_DEPTH_COMPONENT,
            gl.GL_FLOAT,
        )
        # Read buffer and flip X
        dep = np.copy(np.frombuffer(im, np.float32)).reshape(self.shape + (1,))[::-1, :]

        # Convert z-buffer to depth map
        mult = (self.clip_near * self.clip_far) / (self.clip_near - self.clip_far)
        addi = self.clip_far / (self.clip_near - self.clip_far)
        bg = dep == 1
        dep = mult / (dep + addi)
        dep[bg] = 0
        return rgb, np.squeeze(dep)

    def compute_rotation(self, eye_point, look_point):
        up = [0, 1, 0]
        if eye_point[0] == 0 and eye_point[1] != 0 and eye_point[2] == 0:
            up = [0, 0, -1]
        rot = np.zeros((3, 3))
        rot[2] = look_point - eye_point
        rot[2] /= np.linalg.norm(rot[2])
        rot[0] = np.cross(rot[2], up)
        rot[0] /= np.linalg.norm(rot[0])
        rot[1] = np.cross(rot[0], -rot[2])
        return rot.T

    def _validate_pose(self, pose, rot_type="mat"):
        if rot_type == "mat":
            res = pose
            if pose.shape[0] == 3:
                res = np.concatenate(
                    (
                        pose,
                        np.array([0, 0, 0, 1], dtype=np.float32).reshape(1, 4),
                    ),
                    axis=0,
                )
        elif rot_type == "quat":
            res = np.eye(4)
            res[:3, :3] = quat2mat(pose[:4])
            res[:3, 3] = pose[4:7]
        else:
            raise ValueError(f"wrong rot_type: {rot_type}")
        return res  # 4x4

    def draw_detection_boundingbox(
        self,
        pose,
        extents,
        view="center",
        is_gt=False,
        thickness=1.5,
        centroid=0,
        rot_type="mat",
    ):
        """
        centroid: [0,0,0]
        """
        assert view in ["front", "top", "back", "show", "center"]
        pose = self._validate_pose(pose, rot_type=rot_type)
        xsize, ysize, zsize = extents
        # fmt: off
        bb = np.asarray([[-xsize / 2, ysize / 2, zsize / 2], [xsize / 2, ysize / 2, zsize / 2],
                         [-xsize / 2, -ysize / 2, zsize / 2], [xsize / 2, -ysize / 2, zsize / 2],
                         [-xsize / 2, ysize / 2, -zsize / 2], [xsize / 2, ysize / 2, -zsize / 2],
                         [-xsize / 2, -ysize / 2, -zsize / 2], [xsize / 2, -ysize / 2, -zsize / 2]])
        # Set up rendering data
        bb += centroid
        if is_gt:
            colors = [[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1]]
        else:
            colors = [[0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0], [1, 1, 0]]
        # fmt: on
        indices = [
            0,
            1,
            0,
            2,
            3,
            1,
            3,
            2,
            4,
            5,
            4,
            6,
            7,
            5,
            7,
            6,
            0,
            4,
            1,
            5,
            2,
            6,
            3,
            7,
        ]

        vertices_type = [
            ("a_position", np.float32, 3),
            ("a_color", np.float32, 3),
        ]
        collated = np.asarray(list(zip(bb, colors)), vertices_type)

        self.program_bbox.bind(gloo.VertexBuffer(collated))

        # Flip from our system and .T since OpenGL is column-major
        self.program_bbox["u_model"] = (self.opengl_zdir_neg.dot(pose)).T
        self.program_bbox["u_view"] = self.view[view].T
        self.program_bbox["u_projection"] = self.mat_proj

        gloo.set_line_width(width=thickness)
        self.program_bbox.draw("lines", gloo.IndexBuffer(indices))
        gloo.set_line_width(width=1.0)

    def draw_camera(
        self,
        pose=None,
        color=[0, 1, 0],
        scaler=1.0,
        view="center",
        rot_type="mat",
    ):
        if pose is None:
            pose = np.eye(4)
        else:
            pose = self._validate_pose(pose)

        assert view in ["front", "top", "back", "show"]

        cam = Camera3D(color=color, scaler=scaler)

        # View matrix (transforming the coordinate system from OpenCV to OpenGL camera space)
        # Flip from our system and .T since OpenGL is column-major
        mv = (self.opengl_zdir_neg.dot(pose)).T

        self.program_bbox.bind(cam.vertex_buffer)

        self.program_bbox["u_model"] = mv
        self.program_bbox["u_view"] = self.view[view].T
        self.program_bbox["u_projection"] = self.mat_proj

        gloo.set_line_width(width=2.5)
        self.program_bbox.draw("lines", cam.index_buffer)
        gloo.set_line_width(width=1.0)

    def draw_pointcloud(self, points, colors=None, s_color=None, radius=1.0, view="center"):

        assert view in ["center", "front", "top", "back", "show"]

        points = np.copy(points)

        if colors is None:
            if s_color is None:
                colors = np.asarray(
                    np.ones((points.shape[0], 1)) * [1, 0, 0, 1],
                    dtype=np.float32,
                )
            else:
                colors = np.asarray(
                    np.ones((points.shape[0], 1)) * [1, 0, 0, 1],
                    dtype=np.float32,
                )

        radius = np.ones((points.shape[0]), dtype=np.float32) * radius
        # fmt: off
        data = np.zeros(points.shape[0], [("a_position", np.float32, 3), ("a_bg_color", np.float32, 4),
                                          ("a_fg_color", np.float32, 4), ("a_size", np.float32, 1)])
        # fmt: on

        data["a_position"] = points
        data["a_size"] = radius
        data["a_fg_color"] = 0, 0, 0, 0.5
        data["a_fg_color"] = colors

        self.program_pcl.bind(gloo.VertexBuffer(data))
        self.program_pcl["u_linewidth"] = 0.75
        self.program_pcl["u_antialias"] = 1.00
        self.program_pcl["u_model"] = self.opengl_zdir_neg.dot(np.eye(4)).T
        self.program_pcl["u_view"] = self.view[view].T
        self.program_pcl["u_projection"] = self.mat_proj

        self.program_pcl.draw("points")

    def draw_background(self, image):
        """bgr image."""
        self.program_bg["u_tex"] = gloo.Texture2D(image)
        self.program_bg.bind(self.bg_vbuffer)
        self.program_bg.draw("triangles", self.bg_ibuffer)
        gloo.clear(color=False, depth=True)  # Clear depth

    def draw_model(
        self,
        model_or_id,
        pose,
        ambient=0.5,
        specular=0,
        shininess=1,
        light_dir=(0, 0, -1),
        light_col=(1, 1, 1),
        view="center",
        rot_type="mat",
    ):
        """
        pose: 4x4
        """
        assert view in ["front", "top", "back", "center", "show"]
        pose = self._validate_pose(pose, rot_type=rot_type)
        # View matrix (transforming the coordinate system from OpenCV to OpenGL camera space)
        # Flip from our system and .T since OpenGL is column-major
        m = (self.opengl_zdir_neg.dot(pose)).T

        if isinstance(model_or_id, int):
            # how to avoid re-binding for existing models???
            assert self.models is not None, self.models
            model = self.models[model_or_id]
        else:
            model = model_or_id

        used_program = self.program_col
        if model.texcoord is not None:
            used_program = self.program_tex
            used_program["u_tex"] = model.texture

        used_program.bind(model.vertex_buffer)
        used_program["u_view"] = self.view[view].T
        used_program["u_projection"] = self.mat_proj
        used_program["u_light_dir"] = light_dir
        used_program["u_light_col"] = light_col
        used_program["u_ambient"] = ambient
        used_program["u_specular"] = specular
        used_program["u_shininess"] = shininess

        used_program["u_model"] = m

        used_program.draw("triangles", model.index_buffer)

    def show_scene(self, points, colors, models, poses):
        angular_err, trans_err = np.inf, np.inf

        camera_locations = np.asarray([[7.0, 7.0, 10.0]])
        lookat_locations = np.asarray([[0.0, 0.0, 20.0]])

        # for pose in poses:
        #    lookat = pose[:3, 3].copy()
        #    position = lookat.copy()
        #    position[0] = 0.

        #    camera_locations.append(position)
        #    lookat_locations.append(lookat)

        view = np.eye(4)
        steps = 300

        for path_idx in range(len(camera_locations)):
            cam_loc, lookat_loc = (
                camera_locations[path_idx],
                lookat_locations[path_idx],
            )

            t_start = np.copy(view[:3, 3])
            t_end = cam_loc

            for i in range(steps):

                view[:3, :3] = np.eye(3)

                eye_pos = t_start + ((t_end - t_start) * (i / steps))

                rot = self.compute_rotation(eye_pos, lookat_loc).T
                view[:3, :3] = rot
                view[:3, 3] = -rot @ eye_pos

                print(view)

                self.view["show"] = view.copy()

                flip = np.eye(4)
                self.view["show"] = flip.dot(self.view["show"])

                self.clear()
                self.draw_pointcloud(points, colors, view="show")

                for moodel, pose in zip(models, poses):
                    self.draw_model(moodel, pose, view="show")

                cv2.imshow("show", self.finish()[0])
                cv2.waitKey(10)

    def projective_matrix(self, cam, x0, y0, w, h, nc, fc):

        q = -(fc + nc) / float(fc - nc)
        qn = -2 * (fc * nc) / float(fc - nc)

        # Draw our images upside down, so that all the pixel-based coordinate systems are the same
        # fmt: off
        proj = np.array([
            [2 * cam[0, 0] / w, -2 * cam[0, 1] / w, (-2 * cam[0, 2] + w + 2 * x0) / w, 0],
            [0, 2 * cam[1, 1] / h, (2 * cam[1, 2] - h + 2 * y0) / h, 0],
            [0, 0, q, qn],  # This row is standard glPerspective and sets near and far planes
            [0, 0, -1, 0]
        ])
        # fmt: on

        # Compensate for the flipped image
        return proj.T

if __name__ == "__main__":
    from transforms3d.axangles import axangle2mat
    import time
    from lib.vis_utils.image import vis_image_mask_bbox_cv2
    from tqdm import tqdm
    import matplotlib.pyplot as plt

    import ref

    ref_key = "lm_full"
    data_ref = ref.__dict__[ref_key]

    classes = data_ref.objects
    model_paths = data_ref.model_paths
    # models = load_models()

    cam = data_ref.camera_matrix
    width = data_ref.width
    height = data_ref.height

    obj_ids = [i for i in range(len(model_paths))]

    R1 = axangle2mat((1, 0, 0), angle=0.5 * np.pi)
    R2 = axangle2mat((0, 0, 1), angle=-0.7 * np.pi)
    R = np.dot(R1, R2)
    t = np.array([0, 0, 0.7], dtype=np.float32)
    pose = np.hstack([R, t.reshape((3, 1))])
    pose1 = np.hstack([R, 0.1 + t.reshape((3, 1))])
    pose2 = np.hstack([R, t.reshape((3, 1)) - 0.1])
    pose3 = np.hstack([R, t.reshape((3, 1)) - 0.05])
    pose4 = np.hstack([R, t.reshape((3, 1)) + 0.05])

    # tensor_kwargs = {'device': torch.device('cuda'), 'dtype': torch.float32}
    # image_tensor = torch.empty((height, width, 4), **tensor_kwargs).detach()
    ren = Renderer(
        size=(width, height),
        cam=cam,
        model_paths=model_paths,
        scale_to_meter=data_ref.vertex_scale,
    )

    # rendering
    runs = 0
    t_render = 0
    for j in tqdm(range(200)):
        for obj_id, cls_name in enumerate(classes):
            t0 = time.perf_counter()

            poses = [pose, pose1, pose2, pose3, pose4]
            obj_ids = [obj_id, obj_id, obj_id, obj_id, obj_id]
            # poses = [pose]
            # obj_ids = [obj_id]
            ren.clear()
            # ren.draw_background(background)
            for i in range(len(poses)):
                # cur_model = models[obj_ids[i]]
                cur_model = ren.models[obj_ids[i]]
                ren.draw_model(obj_ids[i], poses[i])
                ren.draw_detection_boundingbox(
                    poses[i],
                    [cur_model.xsize, cur_model.ysize, cur_model.zsize],
                )
            im, depth = ren.finish()
            # ren.finish(image_tensor=image_tensor)

            # im = (im.cpu().numpy() + 0.5).astype(np.uint8)
            t_render += time.perf_counter() - t0
            runs += 1
            if False:
                fig = plt.figure(frameon=False, dpi=200)
                plt.subplot(1, 2, 1)
                # im = (image_tensor[:, :, :3].cpu().numpy() + 0.5).astype('uint8')
                plt.imshow(im[:, :, [2, 1, 0]])  # rgb
                plt.axis("off")
                plt.title("{} color".format(cls_name))

                plt.subplot(1, 2, 2)
                plt.imshow(depth)
                plt.axis("off")
                plt.title("{} depth".format(cls_name))

                plt.show()
    print("{}s {}fps".format(t_render / runs, runs / t_render))
    # 5 objs: 0.01251343576113383s 79.91410345557973fps
    # 1 obj:  0.00796224382718404s 125.59273763833781fps


import fpylll

def reduce(matrix: list[list[int]]) -> list[list[int]]:
  """Performs a lattice reduction.

  This function calls LLL to find a reduce basis of the input.

  Args:
    matrix: the integer lattice to reduce

  Returns:
    the reduce basis. Typically the first row of the result is one of the
    shortest vectors in the lattice
  """
  # Convert the input to python integers if necessary.
  # This is done to avoid potential problems, when the underlying library uses
  # different int implementations. (E.g. using sagemath instead of fpylll had
  # previously lead to problems).
  tmp = [[int(v) for v in row] for row in matrix]
  m = fpylll.IntegerMatrix.from_matrix(tmp)
  reduced = fpylll.LLL.reduction(m)
  res = [[0] * reduced.ncols for _ in range(reduced.nrows)]
  reduced.to_matrix(res)
  return res


class Student:
    def __init__(self, Name, ID, Major):
        self.Name = Name
        self.ID = ID
        self.Major = Major

    def print_student_details(self):
        print(self.Name, self.ID, self.Major)

    def add_course(self, course):
        self.course = course
        print(f'{self.Name}選了{course}')

    def retreat(self, retreat):
        if(self.course == retreat):
            print(f'{retreat}已退選')

obj1 = Student("大A", 'C01', '資工')
obj2 = Student("小B", 'C02', '電機')

obj1.print_student_details()
obj2.print_student_details()

obj1.add_course('程式設計')
obj1.retreat('程式設計')
obj1.add_course('物件導向')


import BaseHTTPServer
import urlparse
import socket
import thread
import wx
from service.settings import CRESTSettings

import logging

logger = logging.getLogger(__name__)

HTML = '''
<!DOCTYPE html>

    <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
    <title>pyfa Local Server</title>

pyfa

If you see this message then it means you should be logged into CREST. You may close this window and return to the application.

'''

# https://github.com/fuzzysteve/CREST-Market-Downloader/
class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/favicon.ico":
            return
        parsed_path = urlparse.urlparse(self.path)
        parts = urlparse.parse_qs(parsed_path.query)
        self.send_response(200)
        self.end_headers()
        self.wfile.write(HTML)

        wx.CallAfter(self.server.callback, parts)

    def log_message(self, format, *args):
        return

# http://code.activestate.com/recipes/425210-simple-stoppable-server-using-socket-timeout/
class StoppableHTTPServer(BaseHTTPServer.HTTPServer):

    def server_bind(self):
        BaseHTTPServer.HTTPServer.server_bind(self)
        self.settings = CRESTSettings.getInstance()

        # Allow listening for x seconds
        sec = self.settings.get('timeout')
        logger.debug("Running server for %d seconds", sec)

        self.socket.settimeout(0.5)
        self.max_tries = sec / self.socket.gettimeout()
        self.tries = 0
        self.run = True

    def get_request(self):
        while self.run:
            try:
                sock, addr = self.socket.accept()
                sock.settimeout(None)
                return (sock, addr)
            except socket.timeout:
                pass

    def stop(self):
        self.run = False

    def handle_timeout(self):
        #logger.debug("Number of tries: %d"%self.tries)
        self.tries += 1
        if self.tries == self.max_tries:
            logger.debug("Server timed out waiting for connection")
            self.stop()

    def serve(self, callback):
        self.callback = callback
        while self.run:
            try:
                self.handle_request()
            except TypeError:
                pass
        self.server_close()

if __name__ == "__main__":
    httpd = StoppableHTTPServer(('', 6461), AuthHandler)
    thread.start_new_thread(httpd.serve, ())
    raw_input("Press <RETURN> to stop server\n")
    httpd.stop()


import paho.mqtt.client as mqtt
import random
import time
client=mqtt.Client()
client.connect('broker.hivemq.com',1883)
print('Broker Connected')

while True:
    hum=random.randint(40,100)
    temp=random.randint(20,40)
    data={}
    data['hum']=hum
    data['temp']=temp
    client.publish('rcew/ml',str(data))
    print(data)
    time.sleep(2)


# © 2016 ForgeFlow S.L. (https://www.forgeflow.com)
# © 2016 Serpent Consulting Services Pvt. Ltd.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo.exceptions import UserError
from odoo.tests import Form, common

class TestPurchaseRequisitionOperatingUnit(common.TransactionCase):

    # Test Cases:
    # - Create Purchase Requisition
    #   - Change operating_unit_id will change picking_type_id correctly
    #   - User can't use picking_type_id not belong to same operating_unit_id
    # - When create PO, the OU and picking_type_id will be pass correctly

    def setUp(self):
        super(TestPurchaseRequisitionOperatingUnit, self).setUp()
        self.pr_model = self.env["purchase.requisition"]
        self.pr_line_model = self.env["purchase.requisition.line"]
        self.po_model = self.env["purchase.order"]
        # company
        self.company = self.env.ref("base.main_company")
        self.grp_acc_manager = self.env.ref("account.group_account_manager")
        # Main Operating Unit
        self.ou1 = self.env.ref("operating_unit.main_operating_unit")
        # B2B Operating Unit
        self.b2b = self.env.ref("operating_unit.b2b_operating_unit")
        # B2C Operating Unit
        self.b2c = self.env.ref("operating_unit.b2c_operating_unit")
        # Partner
        self.partner1 = self.env.ref("base.res_partner_1")
        # Products
        self.product1 = self.env.ref("product.product_product_7")
        self.product2 = self.env.ref("product.product_product_9")

    def _create_pr(self):
        line_products = [
            (self.product1, 1000),
            (self.product2, 500),
        ]
        lines = []
        for product, qty in line_products:
            line_values = {
                "product_id": product.id,
                "product_qty": qty,
                "product_uom_id": product.uom_po_id.id,
            }
            lines.append((0, 0, line_values))
        pr_vals = {
            "type_id": self.env.ref("purchase_requisition.type_multi").id,
            "operating_unit_id": self.ou1.id,
            "line_ids": lines,
        }
        # Create PR
        self.pr = self.pr_model.sudo().create(pr_vals)

    def test_create_purchase_requisition(self):
        self._create_pr()
        # Change OU, result in warning
        with self.assertRaises(UserError):
            self.pr.operating_unit_id = self.b2c
        # on_change OU
        pr_mock = self.pr_model.new()
        pr_mock.operating_unit_id = self.b2c
        pr_mock._onchange_operating_unit_id()
        picktype = pr_mock.picking_type_id  # on change result
        self.assertEqual(
            self.b2c,
            picktype.warehouse_id.operating_unit_id,
            "Purchase Requisition and the Warehouse of picking "
            "type does not belong to same Operating Unit.",
        )
        # Now OU and Picking Type should be in line as b2c
        self.pr.write(
            {
                "picking_type_id": pr_mock.picking_type_id.id,
                "warehouse_id": pr_mock.warehouse_id.id,
                "operating_unit_id": pr_mock.operating_unit_id.id,
            }
        )
        # Confirm Call
        self.pr.action_in_progress()
        self.assertEqual(self.pr.state, "in_progress", "State not changed to Confirmed")
        # Create PO
        ctx = {"default_requisition_id": self.pr.id, "default_user_id": False}
        view_id = "purchase.purchase_order_form"
        with Form(
            self.env["purchase.order"].with_context(ctx), view=view_id
        ) as purchase:
            purchase.partner_id = self.partner1
        self.po = purchase.save()
        self.assertEqual(
            self.po.operating_unit_id,
            self.pr.operating_unit_id,
            "Operating Unit is not correctly passed to PO",
        )
        self.assertEqual(
            self.po.picking_type_id,
            self.pr.picking_type_id,
            "Picking Type is not correctly passed to PO",
        )


from django.urls import path, re_path
from app import views

urlpatterns = [
    # Matches any html file - to be used for gentella
    # Avoid using your .html in your resources.
    # Or create a separate django app.
    # re_path(r'^.*\.html', views.gentella_html, name='gentella'),

    # The home page
    path('', views.index, name='index'),
    path('search',views.search,name='search'),
    path('stock/<str:pk>',views.stock,name='stock'),
    path('get/ajax/tweets', views.loadtweets, name = "loadtweets"),
    path('get/ajax/news', views.loadnews, name = "loadnews"),
    path('get/ajax/reddit', views.loadreddit, name = "loadreddit"),
    path('get/ajax/stock', views.loadstock, name = "loadstock")

]


guess_num = 0
secrete_num = 20
print("Guess number between 1 - 50")

def guessNum(num):
    num = int(input("Enter a number: "))
    if num <=50:
       if guessNum == secrete_num:
           print("Congratulations, you win!")
       else:
           print("sorry your guess is outside the range")

print(guessNum(secrete_num))


data = loadStrings("https://raw.githubusercontent.com/jpatokal/openflights/master/data/routes.dat")
table = loadTable("http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv" , "header")
lonC = 10.4806
latC = 66.9036
r = 100
angle = 0
points = []
mags = []

def setup():
    size(500,500,P3D)
    smooth()

    for i,line in enumerate(data[:100]):
        nl = split(line, ',')
        #print  str(i) + " departure is {} arrival is {}".format(nl[2],nl[4])

    for row in table.rows():
        lat = row.getFloat("latitude")
        lon = row.getFloat("longitude")
        mag = row.getFloat("mag")

        #print lon,lat
        pt = Coor2Pt(lon, lat)
        #print pt
        points.append(pt)
        mags.append(mag)

def draw():
    global angle
    global table
    background(51)

    translate(height*0.5,width*0.5)
    lights()
    rotateY(angle)
    noStroke()
    fill(255,0,0)
    sphereDetail(25)
    sphere(r+2)
    angle += 0.05

    for i,pt in enumerate(points):
        pushMatrix()
        translate(pt[0],pt[1],pt[2])
        fill(255,255,255)
        sphereDetail(5)
        sphere(mags[i])
        popMatrix()

def Coor2Pt(lon,lat):
    theta = radians(lat) + PI/2;
    phi = radians(lon) + PI;

    x = r * sin(theta) * cos(phi)
    y = r * sin(theta) * sin(phi)
    z = r * cos(theta)

    return (x,y,z)


text="""Interesting facts about the Moon. The Moon is Earth's only satellite.
There are several interesting facts about the Moon and how it affects life here on Earth.
On average, the Moon moves 4cm away from the Earth every year.
This yearly drift is not significant enough to cause immediate effects on Earth.
The highest daylight temperature of the Moon is 127 C.""".replace("\n", "")
#divide el texto en cada oracion para trabajar con su contenido
sentencias = text.split('. ')

print(sentencias)

#define algunas palabras clave para busqueda que te ayudaran a determinar si una oracion contiene un hecho.
palabras_clave = ['average','temperature','distance']

#Crea un bucle para imprimir solo datos sobre la Luna que esten relacionados con las palabras clave definidas anteriormente:
print("\nPalabras clave sobre la Luna: ")
for item in sentencias:
    for palabra_clave in palabras_clave:
        if palabra_clave in item:
            print("La palabra clave es: '"+palabra_clave+"' Texto: '"+item+"'")

#actualiza el bucle(ciclo) para cambiar C a Celsius
print("\nCambiar C a Celsius: ")
for item in sentencias:
    print("Texto: '"+item+"'")
    if item.count(' C')>0:
        print("\tNuevo texto: "+item.replace(' C', ' Celsius'))
        break
    else:
        print("\tSin cambios")


import os
import argparse
import asyncio
import time
from datetime import timedelta

import logging, logging.handlers
_LOGGER = logging.getLogger(__name__)

import discord

EMOJI_ASTRAY = "\N{ANGRY FACE}"
EMOJI_ACTIVE = "\N{FACE WITH MONOCLE}"
EMOJI_FINISHED = "\N{SHRUG}\N{ZWJ}\N{FEMALE SIGN}\uFE0F"
EMOJI_IGNORED = "\N{POUTING FACE}"
EMOJI_SPECTRUM = frozenset({
    EMOJI_ASTRAY,
    EMOJI_ACTIVE,
    EMOJI_FINISHED,
    EMOJI_IGNORED,
})

QUEUE_RULES_PREFIX = """
В этом чате установлены следующие правила:
""".strip('\n')
QUEUE_RULES_PREFIX_OTHER = """
В чате очереди установлены следующие правила:
""".strip('\n')
QUEUE_RULES = f"""
\N{HEAVY CHECK MARK} в очередь нужно записаться, указав задачи, которые вы хотите сдать;
{EMOJI_IGNORED} нельзя писать несколько сообщений; можно редактировать или удалять старое;
{EMOJI_ASTRAY} записавшись, нужно подключиться к голосовому каналу очереди (с выключенным микрофоном);
{EMOJI_ACTIVE} свободный преподаватель переключит вас в свой канал для разговора;
{EMOJI_FINISHED} поcле разговора сообщение из очереди нужно удалить.
""".strip('\n')

QUEUE_ALGORITHM = f"""
Я буду отмечать сообщения учеников в текстовом чате очереди одной из реакций {EMOJI_IGNORED}, {EMOJI_ASTRAY}, {EMOJI_ACTIVE}, {EMOJI_FINISHED} (или ни одной из них). Наличие реакции означает, что сообщение нерелевантно по той или иной причине.

{EMOJI_IGNORED} это не первое сообщение ученика в очереди;
{EMOJI_ASTRAY} ученик не подключен ни к какому голосовому каналу;
{EMOJI_ACTIVE} ученик подключен (или его подключили) к некоторому голосовому каналу, кроме канала очереди;
{EMOJI_FINISHED} ученик подключался (или его подключали) к некоторому голосовому каналу, кроме канала очереди (если у ученика есть несколько сообщений в разных чатах очереди, будет отмечено только одно из них — по умолчанию в первом канале по списку).

Самостоятельно выйти из «прослушанного» состояния ({EMOJI_FINISHED}) ученик может, только удалив свое сообщение. Если преподаватель видит, что это состояние наступило некорректно (например, ученик случайно зашёл не в тот канал, или хотел сдавать определенному преподавателю), он может стереть все реакции у сообщения, вернув его к изначальному состоянию.

Сообщения от преподавателей игнорируются (кроме команд).

(Определения.
_Преподаватель_ — это участник сервера, у которого есть роль, название которой начинается с «препод» в любом регистре.
_Текстовый чат очереди_ — это любой текстовый чат, название которого начинается с «очередь» в любом регистре, либо находящийся в категории с подобным названием.
_Голосовой канал очереди_ — это любой голосовой канал, название которого начинается с «очередь» в любом регистре, либо находящийся в категории с подобным названием.)
""".strip('\n')

QUEUE_COMMANDS_SHORT = """
Команды: «команды», «правила», «алгоритм», «следующий».
""".strip('\n')

QUEUE_COMMANDS = """
Команды:
«команды» — список команд с описанием;
«правила» — краткие правила очереди;
«алгоритм» — более полное описание алгоритма;
«следующий» — в чате очереди, переместить первого ученика в очереди в голосовой канал к преподавателю.
""".strip('\n')

EDIT_RULES_ON_STARTUP = True

QUEUE_PREFIX = "очередь"
TEXT_QUEUE_PREFIX = QUEUE_PREFIX
VOICE_QUEUE_PREFIX = QUEUE_PREFIX
TEACHER_ROLE_PREFIX = "препод"

TIME_LIMIT_CLEAN  = 60 * 60 * 24 * 7
TIME_LIMIT_ACTIVE = 60 * 60
TIME_EPSILON = 10

class QueueBot(discord.Client):

    def __init__(self, *args, **kwargs):
        kwargs["intents"] = discord.Intents(
            guilds=True, members=True,
            messages=True, voice_states=True, reactions=True )
        super().__init__(*args, **kwargs)
        self.queue_states = dict()
        self.dict_lock = asyncio.Lock()
        self.student_activity = self.StudentActivity()

    async def setup_hook(self):
        self.student_activity.start_monitor()

    async def on_error(self, event, *args, **kwargs):
        def recover_guild(*args, **kwargs):
            return None
        if event == 'on_message':
            def recover_guild(message):
                return message.guild
        elif event == 'on_voice_state_update':
            def recover_guild(member, before, after):
                if isinstance(member, discord.Member):
                    return member.guild
        elif event == 'on_raw_reaction_add' or event == 'on_raw_reaction_remove':
            def recover_guild(payload):
                if payload.guild_id is not None:
                    return self.get_guild(payload.guild_id)
        try:
            guild = recover_guild(*args, **kwargs)
        except Exception as error:
            _LOGGER.exception( "Exception while determining guild in on_error",
                exc_info=True )
        prefix = f"Exception in {event}"
        if guild is not None:
            prefix += f" (guild “{guild.name}”)"
        _LOGGER.exception(prefix, exc_info=True)

    class QueueState:

        def __init__(self):
            self.messages = dict()
            self.finished = set()
            self.update()
            self.lock = asyncio.Lock()

        def update(self):
            self.mtime = time.monotonic()

    async def queue_state(self, guild, member):
        if not isinstance(member, int):
            if guild is None:
                guild = member.guild
            member = member.id
        if not isinstance(guild, int):
            guild = guild.id
        async with self.dict_lock:
            try:
                guild_states = self.queue_states[guild]
            except KeyError:
                guild_states = self.queue_states[guild] = dict()
            try:
                state = guild_states[member]
            except KeyError:
                state = guild_states[member] = self.QueueState()
                asyncio.get_running_loop().create_task(
                    self.clean_queue_state(guild, member, state) )
            return state

    async def clean_queue_state(self, guild_id, member_id, queue_state):
        while True:
            delta = queue_state.mtime + TIME_LIMIT_CLEAN - time.monotonic()
            if delta > 0:
                await asyncio.sleep(delta + TIME_EPSILON)
                continue
            break
        async with self.dict_lock:
            if guild_id not in self.queue_states:
                return
            guild_states = self.queue_states[guild_id]
            if member_id not in guild_states:
                return
            if guild_states[member_id] is not queue_state:
                return
            del guild_states[member_id]
            if not guild_states:
                del self.queue_states[guild_id]
        await self.vandalize_queue_state( guild_id, member_id,
            queue_state )

    async def vandalize_queue_state(self, guild_id, member_id, queue_state):
        async with queue_state.lock:
            guild = self.get_guild(guild_id)
            if guild is None:
                return
            member = guild.get_member(member_id)
            if member is None:
                return
            for channel_id, message_id in queue_state.messages.items():
                channel = guild.get_channel(channel_id)
                if channel is None:
                    continue
                try:
                    message = await channel.fetch_message(message_id)
                except (discord.NotFound, discord.Forbidden):
                    continue
                await self.message_add_reactions(message, {EMOJI_IGNORED})
            queue_state.messages.clear()
            queue_state.finished.clear()

    class StudentActivity(dict):

        def __init__(self):
            self.awaken = asyncio.Event()
            self.wake_task = None
            self.wake_time = None

        def start_monitor(self):
            asyncio.get_running_loop().create_task(self.monitor())

        async def monitor(self):
            while True:
                await self.awaken.wait()
                self.awaken.clear()
                self.wake_task = self.wake_time = None
                now = time.monotonic()
                gone_inactive = set()
                deltas = list()
                for guild, atime in self.items():
                    delta = atime + TIME_LIMIT_ACTIVE - now
                    if delta <= 0:
                        gone_inactive.add(guild)
                    else:
                        deltas.append(delta)
                for guild in gone_inactive:
                    del self[guild]
                    self.report_inactive(guild)
                if deltas:
                    self.delay_wake(duration=min(deltas) + TIME_EPSILON)
                else:
                    self.report_silence()

        def report_silence(self):
            _LOGGER.info(
                f"All servers have gone inactive" )

        def report_active(self, guild):
            _LOGGER.info(
                f"Server “{guild.name}” (id={guild.id}) has become active" )

        def report_inactive(self, guild):
            guild_name = guild.name
            if guild_name is None:
                guild_name = "<unavailable>"
            _LOGGER.info(
                f"Server “{guild_name}” (id={guild.id}) has gone inactive" )

        def note_guild(self, guild, mtime=None):
            fresh = guild not in self
            now = time.monotonic()
            if mtime is not None:
                age = (discord.utils.utcnow() - mtime).total_seconds()
                if age >= TIME_LIMIT_ACTIVE:
                    return
                now -= age
            if fresh:
                self.report_active(guild)
            self[guild] = now
            if fresh:
                self.delay_wake( now=now,
                    duration=TIME_LIMIT_ACTIVE + TIME_EPSILON )

        def clear_guild(self, guild):
            if guild in self:
                del self[guild]
                self.report_inactive(guild)

        def delay_wake(self, *, now=None, duration):
            if now is None:
                now = time.monotonic()
            wake_time = now + duration
            if self.wake_task is not None:
                if self.wake_time <= wake_time:
                    return
                self.wake_task.cancel()
                self.wake_task = self.wake_time = None
            self.wake_task = asyncio.get_running_loop().create_task(
                self.wait_and_wake(duration) )
            self.wake_time = wake_time

        async def wait_and_wake(self, duration):
            await asyncio.sleep(duration)
            self.awaken.set()

    def member_is_teacher(self, member):
        if not isinstance(member, discord.Member):
            return False
        return any(
            role.name.lower().startswith(TEACHER_ROLE_PREFIX)
            for role in member.roles )

    def text_channel_is_queue(self, text_channel):
        if text_channel.name.lower().startswith(TEXT_QUEUE_PREFIX):
            return True
        category = text_channel.category
        if category is not None and \
                category.name.lower().startswith(QUEUE_PREFIX):
            return True
        return False

    def voice_channel_is_queue(self, voice_channel):
        if voice_channel.name.lower().startswith(VOICE_QUEUE_PREFIX):
            return True
        category = voice_channel.category
        if category is not None and \
                category.name.lower().startswith(QUEUE_PREFIX):
            return True
        return False

    def queue_channels(self, guild):
        for text_channel in guild.text_channels:
            if self.text_channel_is_queue(text_channel):
                yield text_channel

    async def consider_message( self, message, *,
        guild=None, channel=None, member=None,
        queue_state=None, lock_acquired=False,
        historical=False,
    ):
        # return whether something has changed
        if member is None:
            member = message.author
            if not isinstance(member, discord.Member):
                return False
            if member == self.user:
                return False
            if self.member_is_teacher(member):
                return False
        if channel is None:
            channel = message.channel
            if not isinstance(channel, discord.TextChannel):
                return False
            if not self.text_channel_is_queue(channel):
                return False
        if guild is None:
            guild = member.guild
            assert guild == channel.guild
        if queue_state is None:
            if lock_acquired:
                raise RuntimeError("escaping potential deadlock")
            queue_state = await self.queue_state(guild, member)
        if not lock_acquired:
            await queue_state.lock.acquire()
        try:
            messages = queue_state.messages
            finished = queue_state.finished
            emoji = set( reaction.emoji
                for reaction in message.reactions
                if reaction.emoji in EMOJI_SPECTRUM )
            if EMOJI_IGNORED in emoji:
                if channel.id in messages and \
                        messages[channel.id] == message.id:
                    del messages[channel.id]
                    finished.discard(message.id)
                    queue_state.update()
                    return True
                return False
            if channel.id in messages:
                if messages[channel.id] == message.id:
                    if EMOJI_ACTIVE not in emoji:
                        if EMOJI_FINISHED in emoji:
                            if message.id not in finished:
                                finished.add(message.id)
                                queue_state.update()
                                self.student_activity.note_guild(guild)
                                return True
                        else:
                            if message.id in finished:
                                finished.discard(message.id)
                                queue_state.update()
                                self.student_activity.note_guild(guild)
                                return True
                    return False
                old_message_id = messages[channel.id]
                try:
                    old_message = await channel.fetch_message(old_message_id)
                except (discord.NotFound, discord.Forbidden):
                    old_message = None
                if old_message is None:
                    del messages[channel.id]
                    finished.discard(old_message_id)
                elif old_message_id in finished:
                    del messages[channel.id]
                    finished.discard(old_message_id)
                    await self.message_ignore(old_message)
                else:
                    await self.message_add_reactions(message, {EMOJI_IGNORED})
                    return False
            messages[channel.id] = message.id
            if EMOJI_FINISHED in emoji:
                finished.add(message.id)
            queue_state.update()
            if not historical:
                self.student_activity.note_guild(guild)
            else:
                self.student_activity.note_guild( guild,
                    mtime=message.created_at )
            return True
        finally:
            if not lock_acquired:
                queue_state.lock.release()

    async def update_student( self, member, *,
        guild=None, voice=None, voice_arg=False, allow_finish=False,
        queue_state=None, lock_acquired=False,
    ):
        if guild is None:
            guild = member.guild
        if queue_state is None:
            if lock_acquired:
                raise RuntimeError("escaping potential deadlock")
            queue_state = await self.queue_state(guild, member)
        if not lock_acquired:
            await queue_state.lock.acquire()
        try:
            messages = queue_state.messages
            finished = queue_state.finished
            if not voice_arg:
                voice = member.voice
            if voice is None or voice.channel is None:
                status = "astray"
            elif self.voice_channel_is_queue(voice.channel):
                status = "normal"
            else:
                status = "active"
            prospective_finished = set()
            if status == "active":
                if not finished and allow_finish:
                    for channel in self.queue_channels(guild):
                        if channel.id not in messages:
                            continue
                        prospective_finished.add(messages[channel.id])
                        break
                self.student_activity.note_guild(guild)
            garbage = list()
            for channel_id, message_id in messages.items():
                channel = guild.get_channel(channel_id)
                if channel is None or not self.text_channel_is_queue(channel):
                    garbage.append((channel_id, message_id))
                    continue
                try:
                    message = await channel.fetch_message(message_id)
                except (discord.NotFound, discord.Forbidden):
                    garbage.append((channel_id, message_id))
                    continue
                emoji = set()
                if message_id in prospective_finished:
                    finished.add(message_id)
                if status == "active":
                    emoji.add(EMOJI_ACTIVE)
                elif message_id in finished:
                    emoji.add(EMOJI_FINISHED)
                elif status == "astray":
                    emoji.add(EMOJI_ASTRAY)
                await self.message_add_reactions(message, emoji)
            for channel_id, message_id in garbage:
                del messages[channel_id]
                finished.discard(message_id)
            queue_state.update()
        finally:
            if not lock_acquired:
                queue_state.lock.release()

    async def on_ready(self):
        _LOGGER.info(
            f"{self.user} has connected to Discord, "
            f"and manages queues on {len(self.guilds)} servers" )

    async def on_guild_available(self, guild):
        me = guild.me
        _LOGGER.info(
            f"Server “{guild.name}” (id={guild.id}) is available, and "
            f"I am “{me.nick if me.nick is not None else me.name}”" )
        await self.reconsider_guild(guild)

    on_guild_join = on_guild_available

    async def on_guild_unavailable(self, guild):
        guild_name = guild.name
        if guild_name is None:
            guild_name = "<unavailable>"
        _LOGGER.info(
            f"Server “{guild_name}” (id={guild.id}) is unavailable" )
        async with self.dict_lock:
            if guild.id in self.queue_states:
                del self.queue_states[guild.id]
        self.student_activity.clear_guild(guild)

    async def reconsider_guild(self, guild):
        for channel in self.queue_channels(guild):
            await self.reconsider_channel(channel, guild=guild)
        async with self.dict_lock:
            if guild.id not in self.queue_states:
                return
            for member_id, queue_state in self.queue_states[guild.id].items():
                member = guild.get_member(member_id)
                await self.update_student( member, guild=guild,
                    queue_state=queue_state )

    async def reconsider_channel(self, channel, *, guild):
        try:
            prehistoric = discord.utils.utcnow() - timedelta(TIME_LIMIT_CLEAN)
            prehistoric_limit = 7
            async for message in channel.history(limit=None):
                if EDIT_RULES_ON_STARTUP and message.author == self.user:
                    content = message.content
                    if not content.startswith(QUEUE_RULES_PREFIX):
                        continue
                    if content == QUEUE_RULES_PREFIX + "\n" + QUEUE_RULES:
                        continue
                    _LOGGER.info("editing previously posted rules")
                    await message.edit(
                        content=QUEUE_RULES_PREFIX + "\n" + QUEUE_RULES )
                    prehistoric_limit = 0
                    continue
                if message.created_at <= prehistoric:
                    prehistoric_limit -= 1
                    if prehistoric_limit < 0:
                        break
                await self.consider_message( message,
                    guild=guild, channel=channel,
                    historical=True )
        except discord.Forbidden:
            pass

    async def on_message(self, message):
        member = message.author
        channel = message.channel
        if member == self.user:
            return
        if not isinstance(channel, discord.TextChannel):
            return
        if self.user in message.mentions:
            if await self.on_command(message):
                return
        if self.member_is_teacher(member):
            return
        if not self.text_channel_is_queue(channel):
            return
        queue_state = await self.queue_state(None, member)
        async with queue_state.lock:
            changed = await self.consider_message( message,
                member=member, channel=channel,
                queue_state=queue_state, lock_acquired=True )
            if changed:
                await self.update_student( member,
                    queue_state=queue_state, lock_acquired=True )

    async def on_voice_state_update(self, member, before, after):
        if self.member_is_teacher(member):
            return
        if before.channel == after.channel:
            return
        await self.update_student(member, voice=after, allow_finish=True)

    async def on_raw_reaction_add(self, payload):
        if payload.user_id == self.user.id:
            return
        if payload.guild_id is None:
            return
        guild = self.get_guild(payload.guild_id)
        if guild is None:
            return
        channel = guild.get_channel(payload.channel_id)
        if channel is None or not isinstance(channel, discord.TextChannel):
            return
        if not self.text_channel_is_queue(channel):
            return
        try:
            message = await channel.fetch_message(payload.message_id)
        except (discord.NotFound, discord.Forbidden):
            return
        member = message.author
        if member == self.user:
            return
        if self.member_is_teacher(member):
            return
        queue_state = await self.queue_state(guild, member)
        async with queue_state.lock:
            changed = await self.consider_message( message,
                member=member, channel=channel,
                queue_state=queue_state, lock_acquired=True )
            if changed:
                await self.update_student( member,
                    queue_state=queue_state, lock_acquired=True )

    async def on_raw_reaction_remove(self, payload):
        if payload.guild_id is None:
            return
        guild = self.get_guild(payload.guild_id)
        if guild is None:
            return
        channel = guild.get_channel(payload.channel_id)
        if channel is None or not isinstance(channel, discord.TextChannel):
            return
        if not self.text_channel_is_queue(channel):
            return
        try:
            message = await channel.fetch_message(payload.message_id)
        except (discord.NotFound, discord.Forbidden):
            return
        member = message.author
        if member == self.user:
            return
        if self.member_is_teacher(member):
            return
        queue_state = await self.queue_state(guild, member)
        async with queue_state.lock:
            changed = await self.consider_message( message,
                member=member, channel=channel,
                queue_state=queue_state, lock_acquired=True )
            if changed:
                await self.update_student( member,
                    queue_state=queue_state, lock_acquired=True )

    on_raw_reaction_clear_emoji = on_raw_reaction_remove
    on_raw_reaction_clear = on_raw_reaction_remove

    async def message_ignore(self, message):
        try:
            await message.add_reaction(EMOJI_IGNORED)
        except (discord.NotFound, discord.Forbidden):
            return

    async def message_add_reactions(self, message, emoji_set):
        try:
            if not emoji_set <= EMOJI_SPECTRUM:
                unknown_emoji = next(emoji_set - EMOJI_SPECTRUM)
                raise RuntimeError(
                    f"Unrecognised emoji {unknown_emoji} "
                    f"(code {''.join(hex(ord(e)) for ee in unknown_emoji)})" )
            me = self.user
            emoji_add = set(emoji_set)
            reactions_remove = list()
            for reaction in message.reactions:
                if reaction.emoji not in EMOJI_SPECTRUM:
                    continue
                if reaction.emoji in emoji_set:
                    async for user in reaction.users():
                        if user == me:
                            emoji_add.discard(reaction.emoji)
                            break
                else:
                    reactions_remove.append(reaction)
            for emoji in emoji_add:
                await message.add_reaction(emoji)
            for reaction in reactions_remove:
                await reaction.clear()
        except (discord.NotFound, discord.Forbidden):
            return

    async def on_command(self, message):
        # return whether it really should be considered a command
        if not self.member_is_teacher(message.author):
            return False
        channel = message.channel
        if not isinstance(channel, discord.TextChannel):
            return False
        mention_prefix = f"<@!{self.user.id}> "
        if message.content.startswith(mention_prefix):
            command = message.content[len(mention_prefix):]
        else:
            await self.send_help( channel,
                reply_to=message.author, error="Команда не распознана." )
            return True
        if command.lower() == "команды":
            await self.send_help(channel, short=False)
        elif command.lower() == "правила":
            await self.on_command_rules( channel,
                is_queue=self.text_channel_is_queue(channel) )
        elif command.lower() == "алгоритм":
            await self.on_command_algorithm(channel)
        elif command.lower() == "следующий":
            if not self.text_channel_is_queue(channel):
                await channel.send( f"<@!{message.author.id}> "
                    "Команду «следующий» можно запускать "
                    "только в чате очереди.",
                    allowed_mentions=discord.AllowedMentions(
                        everyone=False, roles=False,
                        users=[message.author] )
                )
                return True
            await self.on_command_next(channel, message.author)
            await message.delete()
        else:
            await self.send_help( channel,
                reply_to=message.author,
                error=f"Команда «{command}» не распознана" )
        return True

    async def on_command_rules(self, channel, *, is_queue=False):
        #nick = channel.guild.me.nick
        prefix = QUEUE_RULES_PREFIX if is_queue else QUEUE_RULES_PREFIX_OTHER
        await channel.send( prefix + "\n" + QUEUE_RULES,
            allowed_mentions=discord.AllowedMentions(
                everyone=False, roles=False, users=False )
        )

    async def on_command_algorithm(self, channel):
        await channel.send( QUEUE_ALGORITHM,
            allowed_mentions=discord.AllowedMentions(
                everyone=False, roles=False, users=False )
        )

    async def on_command_next(self, channel, teacher):
        guild = channel.guild
        async for message in channel.history(oldest_first=True):
            member = message.author
            if not isinstance(member, discord.Member):
                continue
            if member == self.user:
                continue
            if self.member_is_teacher(member):
                continue
            queue_state = await self.queue_state(guild, member)
            if channel.id not in queue_state.messages:
                continue
            if queue_state.messages[channel.id] != message.id:
                continue
            if message.id in queue_state.finished:
                continue
            if member.voice is None or member.voice.channel is None:
                return
            break
        else:
            return
        async with queue_state.lock:
            if teacher.voice is None or teacher.voice.channel is None:
                return
            voice_channel = teacher.voice.channel
            if self.voice_channel_is_queue(voice_channel):
                return
            await member.move_to(voice_channel, reason="queue")
            queue_state.finished.add(message.id)

    async def send_help( self, channel,
        *, reply_to=None, error=None, short=True,
    ):
        sentences = []
        if reply_to is not None:
            sentences.append(f"<@!{reply_to.id}>")
        if error is not None:
            sentences.append(error)
        sentences.append(
            f"Формат команд: `@{channel.guild.me.name} <команда>`." )
        sentences.append(QUEUE_COMMANDS_SHORT if short else QUEUE_COMMANDS)
        await channel.send( " ".join(sentences),
            allowed_mentions=discord.AllowedMentions(
                everyone=False, roles=False,
                users=[reply_to] if reply_to is not None else False )
        )

async def main():
    parser = argparse.ArgumentParser("QueueBot for Discord")
    parser.add_argument("--log-level", choices=["DEBUG", "INFO", "WARNING"], default="INFO")
    parser.add_argument("--log-file")
    args = parser.parse_args()
    _LOGGER.setLevel(args.log_level)
    log_formatter = logging.Formatter(
        "%(asctime)s %(message)s",
        "%Y-%m-%d %H:%M:%S" )
    if args.log_file is None:
        log_stream_handler = logging.StreamHandler()
        _LOGGER.addHandler(log_stream_handler)
        log_stream_handler.setFormatter(log_formatter)
    else:
        log_stream_handler = logging.StreamHandler()
        log_stream_handler.setLevel("ERROR")
        log_stream_handler.setFormatter(log_formatter)
        _LOGGER.addHandler(log_stream_handler)
        log_file_handler = logging.handlers.RotatingFileHandler(
            args.log_file,
            maxBytes=(1 << 18), backupCount=1 )
        def antirotator(source, dest):
            with open(source, "wb"):
                pass
        log_file_handler.rotator = antirotator
        log_file_handler.setFormatter(log_formatter)
        _LOGGER.addHandler(log_file_handler)
    _LOGGER.info("starting up")
    _LOGGER.debug("debug output enabled")
    client = QueueBot()
    await client.start(os.getenv('DISCORD_TOKEN'))
    _LOGGER.info("shutting down")

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

# vim: set wrap lbr :


from tkinter import *
from tkinter.ttk import *
from tkinter import filedialog
from PIL import Image, ImageTk
from main import *

# style = Style()
# style.configure("BW.Tlabel", foreground=)

root = Tk()
root.configure(background="#B0BA84")
root.title('Polar bear')
# root.geometry("550x300+500+350")
root.geometry("300x70+650+300")
root.resizable(width=True, height=True)

def open_img():
    filename = filedialog.askopenfilename(title='open')
    # foldername = filedialog.askdirectory(title='open')
    # print(foldername)
    img = Image.open(filename)
    root.geometry("700x570+390+100")
    img = img.resize((450, 450), Image.ANTIALIAS)
    img = ImageTk.PhotoImage(img)
    panel = Label(root, image=img, padding=(0,10,0,0), background="#B0BA84")
    panel.image = img
    panel.pack()

def open_folder():
    filename = filedialog.askopenfilename(title='open')

# btn = Button(root, text="Выбери фото", bg="#266E67", fg="#fff", command=open_img)
frame_btn = Label(root, background="#B0BA84")
# frame_btn.configure(bg="#B0BA84", pady=20)
frame_btn.pack(pady=20)

btn = Button(frame_btn, text="Выберите фото", command=open_img)
btn2 = Button(frame_btn, text="Выберите папку", command=open_folder)
empty = Label(frame_btn, background="#B0BA84", width=5)
btn.pack(side="left")
empty.pack(side="left")
btn2.pack(side="right")

root.mainloop()