from private_method import *

table_sql = """
    INSERT INTO saas_table(tableName, tenantId)
    VALUES ('%s', %d);
"""

field_sql = """
    INSERT INTO saas_field( dataType, fieldName, tableId, fieldNum, tenantId, isIndexed)
    VALUES ( '%s', '%s', %d, %d, %d, %s);
"""

# dataType, fieldName, tableId, fieldNum, tenantId, isIndexed

def create_table(tenantId, tableName, column):
    table_para = (tableName, tenantId)
    sql = table_sql % table_para   # 创建表
    field_num = 0
    db_template(sql)
    count = get_tableID(tenantId, tableName)
    sql = ""
    for i in column:  # 创建列
        tmp = (i[1], i[0], count, field_num, tenantId, i[2])
        sql += (field_sql % tmp)
        field_num += 1
    db_template(sql)

def insert_data(tenantId, tableName, columns, values):
    tableID = get_tableID(tenantId, tableName)
    sql = "insert into saas_data (tableID, tenantId,"
    cnt = 0
    for i in columns:
        num = get_fieldID(tenantId, tableID, i)
        sql += "value" + str(num) + ","
        cnt += 1
    sql = sql[:-1]
    sql += ") values (" + str(tableID) + "," + str(tenantId) + ","
    for i in values:
        sql += "\'" + str(i) + "\',"
    sql = sql[:-1]
    sql += ")"
    db_template(sql)
    sql = "select fieldNum,fieldName from saas_field where tenantId= " + str(tenantId) + " and tableID= " + str(tableID) + " and isIndexed= True"
    field_rows = db_template_return(sql)
    for i in range(0,cnt):
        for row in field_rows:
            if columns[i] == row[1]:
                cnt = get_fieldID(tenantId,tableID, columns[i])
                sql = "select Id from saas_data where tenantId= " + str(tenantId) + " and tableID= " + str(tableID) \
                      + " and value" + str(cnt) + "= \'" + str(values[i]) + "\'"
                id = db_template_return(sql)
                sql = "insert into public.saas_index ( \"tenantId\", \"tableId\" , \"fieldNum\", \"recordId\", \"stringValue\") values("
                sql += str(tenantId) + " , " + str(tableID) + " ," + str(row[0]) + " , " + str(id[0][0]) + " , \'" + str(values[i]) + "\');"
                db_template(sql)

def delete_data(tenantId, tableName, columns, conditions, condition_num):
    tableID = get_tableID(tenantId, tableName)
    sql = "select Id from saas_data where tenantId= " + str(tenantId) + " and tableID= " + str(tableID)
    for i in range(0, condition_num):
        num = get_fieldID(tenantId, tableID, columns[i])
        sql += " and value" + str(num) + " =\'" + str(conditions[i]) + "\'"
    rows = db_template_return(sql)
    for row in rows:
         sql = "delete from saas_index where recordId= " + row[0]
         db_template(sql)

    sql = "delete from saas_data where tenantId= " + str(tenantId) + " and tableID= " + str(tableID)
    for i in range(0,condition_num):
        num = get_fieldID(tenantId, tableID, columns[i])
        sql += " and value" + str(num) + " =\'" + str(conditions[i]) + "\'"
    db_template(sql)

def select_data_all(tenantId, tableName):
    tableID = get_tableID(tenantId, tableName)
    rows = []

    sql = "select fieldNum from saas_field where tenantId= " + str(tenantId) + " and tableID= " + str(tableID)
    field_rows = db_template_return(sql)

    sql = "select "
    for i in field_rows:
        sql += " value" + str(i[0]) + ","
    sql = sql[:-1]
    sql += " from saas_data where tenantId= " + str(tenantId) + " and tableID= " + str(tableID)
    rows = db_template_return(sql)
    return rows

def select_data_condition(tenantId, tableName, columns, num_of_columns, conditions_columns, conditions_operators, conditions, condition_num):
    tableID = get_tableID(tenantId, tableName)
    rows = []
    sql = "select "
    for i in range(num_of_columns):
        if columns[i] == "*":
            sql += "*"
        else:
            num = get_fieldID(tenantId, tableID, columns[i])
            sql += " value" + str(num) + " ,"
    sql = sql[:-1]
    sql += " from saas_data where tenantId= " + str(tenantId) + " and tableID= " + str(tableID)
    for i in range(0, condition_num):
        num = get_fieldID(tenantId, tableID, conditions_columns[i])
        pri = primary_check(tenantId, tableID, conditions_columns[i])
        if pri is True:
            sql += " and Id in ( select \"recordId\" from saas_index where \"stringValue\"" + conditions_operators[i] + " \'" + str(conditions[i]) + "\') "
        else:
            sql += " and value" + str(num) + conditions_operators[i] + "\'" + str(conditions[i]) + "\'"
    rows = db_template_return(sql)
    return rows

def update(tenantId, tableName, columns, values, num_of_columns, conditions_columns, conditions_operators, conditions, condition_num):
    tableID = get_tableID(tenantId, tableName)
    sql = "update saas_data set"
    for i in range(num_of_columns):
        num = get_fieldID(tenantId,tableID,columns[i])
        sql += " value" + str(num) + " = \'" + str(values[i]) + "\',"
    sql = sql[:-1]
    sql += "where tenantId= " + str(tenantId) + " and tableID= " + str(tableID)
    for i in range(0, condition_num):
        num = get_fieldID(tenantId, tableID, conditions_columns[i])
        pri = primary_check(tenantId, tableID, conditions_columns[i])
        if pri is True:
            sql += " and Id in ( select \"recordId\" from saas_index where \"stringValue\"" + conditions_operators[
                i] + " \'" + str(conditions[i]) + "\') "
        else:
            sql += " and value" + str(num) + conditions_operators[i] + "\'" + str(conditions[i]) + "\'"
    db_template(sql)


import tensorflow as tf
import numpy as np
import time
import cv2

import pickle
import utils
import tools
from tensorflow.keras.mixed_precision import experimental as prec
import copy

def count_steps(folder):
    return sum(int(str(n).split("-")[-1][:-4]) - 1 for n in folder.glob("*.npz"))

class Play:
    def __init__(self, env, model_maker, config, training=True):
        self.env = env

        # self.act_space = self.env.action_space

        self._c = config
        self._precision = config.precision
        self._float = prec.global_policy().compute_dtype

        # self.ob, _, _, _ = self.env.step(
        #     self.env._env.action_space.sample()
        # )  # whether it is discrete or not, 0 is proper
        self.ob = self.env.reset()
        self.state = None
        acts = self.env.action_space
        self.random_actor = tools.OneHotDist(tf.zeros_like(acts.low)[None])

        self._c.num_actions = acts.n if hasattr(acts, "n") else acts.shape[0]
        print("self._c.num_actions:", self._c.num_actions)
        # self.batch_size = 16
        self.batch_size = self._c.batch_size
        self.batch_length = (
            self._c.batch_length
        )  # when it is not model-based learning, consider it controling the replay buffer
        self.TD_size = 1  # no TD
        self.play_records = []

        self.advantage = True

        self.total_step = 1
        self.save_play_img = False
        self.RGB_array_list = []
        self.episode_reward = 0
        self.episode_step = 0  # to avoid devide by zero
        self.datadir = self._c.logdir / "episodes"

        self._writer = tf.summary.create_file_writer(
            "./tf_log", max_queue=1000, flush_millis=20000
        )

        if training:
            self.prefill_and_make_dataset()
        else:
            pass

        with tf.device("cpu:1"):
            self._step = tf.Variable(count_steps(self.datadir), dtype=tf.int64)

        self._c.actor_entropy = lambda x=self._c.actor_entropy: tools.schedule(
            x, self._step
        )
        self._c.actor_state_entropy = (
            lambda x=self._c.actor_state_entropy: tools.schedule(x, self._step)
        )
        self._c.imag_gradient_mix = lambda x=self._c.imag_gradient_mix: tools.schedule(
            x, self._step
        )
        self.model = model_maker(self.env, training, self._step, self._writer, self._c)

    def random_policy(self):
        action = self.random_actor.sample()
        return action

    def prefill_and_make_dataset(self):
        # since it casuse error when random choice zero object in self.load_dataset
        self.episodes = utils.load_episodes(
            self.datadir, limit=self._c.max_dataset_steps
        )
        while True:
            self.collect(must_be_whole_episode=True, prefill=True)
            if self.total_step >= self._c.prefill:
                self.reset_total_step()
                break
        self._dataset = iter(utils.load_dataset(self.episodes, self._c))

        print("inspect initial episode!!!!")

        # for i in range(500):

        #     data =  self._dataset()
        #     for j in range(50):
        #         k = 5
        #         # for k in range(10):
        #         obs = (data["obs"][j,k,:,:,:]+0.5)*255
        #         print(i,j,k)

        #         # print('data["obs"].shape:',obs.shape) # (50, 10, 64, 64, 3)
        #         cv2.imshow("obs", obs)
        #         cv2.waitKey(1)

        #     # cv2.destroyAllWindows()

    def act_repeat(self, env, act, rendering=False):
        ob, reward, done, info = self.env.step(act)
        if rendering:
            rgb_array = self.env.render()

        return ob, reward, done, info

    def TD_dict_to_TD_train_data(self, batch_data, advantage=False):
        # list of dict of batch_size,TD_size,{4}
        obs = []
        acts = []
        obp1s = []
        rewards = []
        dones = []
        discounts = []

        if advantage:

            for TD_data in batch_data:
                # TD_size,{4}
                TD_reward = 0
                ob = TD_data[0]["ob"]
                act = TD_data[0]["action"]
                obp1 = TD_data[-1]["obp1"]

                for i in range(len(TD_data)):
                    TD_reward += TD_data[i]["reward"]

                done = TD_data[-1]["done"]
                discount = TD_data[-1]["discount"]

                # print("TD_reward:",TD_reward)

                obs.append(ob)
                acts.append(act)
                obp1s.append(obp1)
                rewards.append(TD_reward)
                dones.append(float(done))
                discounts.append(discount)

            return (
                np.array(
                    obs
                ),  # if self.backward_n_step > 1, [batch_size,backward_n_step+1,obaservation_size]
                np.array(acts),
                np.array(
                    obp1s
                ),  # if self.backward_n_step > 1, [batch_size,backward_n_step+1,obaservation_size]
                np.array(rewards),
                np.array(dones),
                np.array(discounts),
            )

        else:
            for TD_data in batch_data:
                # TD_size,{4}
                TD_reward = 0
                ob = TD_data[0]["ob"]

                for i in range(self.TD_size):
                    TD_reward += TD_data[i]["reward"]
                done = TD_data[-1]["done"]
                obs.append(ob)
                print("TD_reward:", TD_reward)
                rewards.append(TD_reward)
                dones.append(float(done))

            return np.array(obs), np.array(rewards), np.array(dones)

    def collect(self, must_be_whole_episode=True, prefill=False):
        """
        collect end when the play_records full or episode end
        """
        trainsaction = {
            "ob": copy.deepcopy(self.ob),
            "obp1": copy.deepcopy(self.ob),
            "reward": 0.0,
            "done": 0,
            "discount": np.array(1.0),
        }

        episode_record = [trainsaction]
        """
        I call this episode_record since when there's a done for playing, I must end collecting data for
        capturing the end score of an episode, not to mix with next episode start when doing TD.
        It will start to train(in other words, break the loop) in two situation:
        first, episode is done; second, the buffer is full(>self.batch_size * self.batch_length*self.TD_size).
        """
        # while len(episode_record) < self.batch_size * self.batch_length * self.TD_size:
        while True:  # stop only when episoe ends
            # episode = []
            # while True:

            if prefill:
                act = self.random_policy().numpy()  # to get batch dim
                # print("act:",act)

            else:

                obs_data = {
                    "obs": np.array([self.ob]),
                    "obp1s": np.array([self.ob]),
                    "rewards": 0.0,
                    "discounts": 1.0,
                }

                # print('tuple_of_episode_columns[2]:',np.amax(obs_data["obp1s"]))
                # print('tuple_of_episode_columns[2]:',np.amin(obs_data["obp1s"]))
                obs_data = {k: self._convert(v) for k, v in obs_data.items()}

                act, self.state = self.model.policy(obs_data, self.state, training=True)

                # print('tuple_of_episode_columns[2](after):',np.amax(obs_data["obp1s"]))
                # print('tuple_of_episode_columns[2](after):',np.amin(obs_data["obp1s"]))

                act = act.numpy()
                # print("act:",act)

            if self._c.is_discrete:
                argmax_act = np.argmax(act, axis=-1)

            # # save play img

            # if self.episode_step % 50000 == 0:
            #     self.save_play_img = True

            # if self.save_play_img == True:
            #     self.RGB_array_list.append(self.env.render())

            # if len(self.RGB_array_list) > 500:

            #     for i in range(len(self.RGB_array_list)):
            #         play_img = self.RGB_array_list[i]
            #         play_img = cv2.cvtColor(play_img, cv2.COLOR_RGB2BGR)

            #         cv2.imwrite("./train_gen_img/play_img_{}.jpg".format(i), play_img)
            #     self.RGB_array_list = []
            #     self.save_play_img = False

            ob, reward, done, info = self.act_repeat(
                self.env, argmax_act[0]
            )  # no repear any more

            if not prefill:
                self._step.assign_add(1)

            trainsaction = {
                "ob": copy.deepcopy(self.ob),
                "obp1": copy.deepcopy(ob),
                "action": copy.deepcopy(act[0]),
                "reward": copy.deepcopy(reward),
                "done": copy.deepcopy(done),
                "discount": np.array(
                    1 - float(done)
                ),  # it means when done, discount = 1, else 0.
            }  # ob+1(obp1) for advantage method
            # if self.episode_step < 50000:

            if self.episode_step >= self._c.time_limit:
                print("pre-set!")
                done = True
                trainsaction["done"] = done
                trainsaction["discount"] = np.array(1.0).astype(np.float32)

            episode_record.append(trainsaction.copy())
            # print(
            #     "collecting_data!!:",
            #     self.episode_step,
            #     "len(episode_record):",
            #     len(episode_record),
            # )

            self.ob = ob

            # to coumpute average reward of each step
            self.episode_reward += reward
            self.episode_step += 1  # to avoid devide by zero
            self.total_step += 1
            # print("self.total_step:", self.total_step)

            if prefill == True:
                self.episode_step = 0
                # self.total_step = 1

            else:
                if self._step.numpy() % self._c.train_every == 0:
                    self.dreaming_update()
                    print("update complete!")
                else:
                    pass

            if done:
                print("game done!!")
                # self.env.reset()
                # self.ob, _, _, _ = self.env.step(
                #     self.env._env.action_space.sample()
                # )  # whether it is discrete or not, 0 is proper
                self.ob = self.env.reset()
                self.episode_step = 0

                # to make first trasaction with zero action
                for key, value in episode_record[1].items():
                    if key not in episode_record[0]:
                        # print("episode_record[0] doesn't have key ", key)
                        episode_record[0][key] = 0 * value
                        # print("Now it is:",episode_record[0][key])

                if not prefill:
                    # for dreamer, it need to reset state at end of every episode
                    if self.state is not None and np.array([done]).any():
                        mask = tf.cast(1 - np.array([done]), self._float)[:, None]
                        self.state = tf.nest.map_structure(
                            lambda x: x * mask, self.state
                        )
                    else:
                        self.state = None

                break

        """
        move "TD_dict_to_TD_train_data" before update
        """
        if len(episode_record) > 1:
            # tranfer data to TD dict
            for i in range((len(episode_record) // self.TD_size)):

                TD_data = episode_record[i * self.TD_size : (i + 1) * self.TD_size]
                if (
                    len(TD_data) != self.TD_size
                ):  # to deal with +1 causing not enough data of a TD size
                    print("reverse take for taking just to end of episode")
                    TD_data = episode_record[-self.TD_size :]
                    import pdb
                    pdb.set_trace()
                self.play_records.append(TD_data)
                # so the structure of self.play_records is:
                # (batch_size* batch_length*TD_size or greater , TD_size)

            tuple_of_episode_columns = self.TD_dict_to_TD_train_data(
                self.play_records, True
            )  # for Dreamer, the TD = 1

            dict_of_episode_record = {
                "obs": tuple_of_episode_columns[0],
                "actions": tuple_of_episode_columns[1],
                "obp1s": tuple_of_episode_columns[2],
                "rewards": tuple_of_episode_columns[3],
                "dones": tuple_of_episode_columns[4],
                "discounts": tuple_of_episode_columns[5],
            }
            dict_of_episode_record = {
                k: self._convert(v) for k, v in dict_of_episode_record.items()
            }

            # reset the inner buffer
            filename = utils.save_episode(self.datadir, dict_of_episode_record)
            # if self.model.total_step % 100:

            self.post_process_episodes(self.episodes, filename, dict_of_episode_record)

            episode_record = []
            self.post_process_play_records()
        else:
            episode_record = []
            self.post_process_play_records()
            print("not enough data!!")

    def _convert(self, value):
        value = np.array(value)
        if np.issubdtype(value.dtype, np.floating):
            dtype = {16: np.float16, 32: np.float32, 64: np.float64}[self._precision]
        elif np.issubdtype(value.dtype, np.signedinteger):
            dtype = {16: np.int16, 32: np.int32, 64: np.int64}[self._precision]
        elif np.issubdtype(value.dtype, np.uint8):
            dtype = np.uint8
        else:
            raise NotImplementedError(value.dtype)
        return value.astype(dtype)

    def post_process_play_records(self):
        self.play_records = []

    def post_process_episodes(self, cache, episode_name, episode):
        total = 0
        length = len(episode["rewards"]) - 1
        score = float(episode["rewards"].astype(np.float64).sum())
        video = episode["obp1s"]

        for key, ep in reversed(sorted(cache.items(), key=lambda x: x[0])):

            if total <= self._c.max_dataset_steps - length:
                total += len(ep["rewards"]) - 1
            else:
                del cache[key]

        cache[str(episode_name)] = episode

        step = count_steps(self.datadir)

        with self._writer.as_default():
            tf.summary.scalar(
                "dataset_size",
                total + length,
                step=step * self._c.action_repeat,
            )  # control by model.total_step, record the env total step

            tf.summary.scalar(
                "train_episodes",
                len(cache),
                step=step * self._c.action_repeat,
            )  # control by model.total_step, record the env total step

            tf.summary.scalar(
                "train_return",
                score,
                step=step * self._c.action_repeat,
            )  # control by model.total_step, record the env total step

            tf.summary.scalar(
                "train_length",
                length,
                step=step * self._c.action_repeat,
            )  # control by model.total_step, record the env total step

            print("save train_policy!!!!")
            tools.video_summary(
                "train_policy", np.array(video[None]), step * self._c.action_repeat
            )

        print("the episodes size now is:", total + length, "steps")

    # @tf.function
    def dreaming_update(self):
        """
        using collect function
        1. collect real play record(this should from collect function)
           get (batch,batch_length,feature_size) data

        when geting enough data
        2. using the real play record, batch by batch process
        3. in each batch process:
            world model stage
            a. do extract embed from data
            b. from embed to feat
            c. using feat to reconstruct observation
            d. get world model gradient

            actor stage
            e. do imagine step to horizon, for actor and critic:
            for example:
                from (batch,batch_length,feature_size)  to (batch, horizon,batch_length,feature_size)
            f. get value
            g. do dreamer lambda return
            h. maximize lambda return

            critic stage
            i. make value and lambda return closer as much as possible

        4. in each batch process, after imagine step, do update actor and critic
        """
        data = next(self._dataset)

        data = utils.preprocess(data, self._c)

        obs, actions, obp1s, rewards, dones, discounts = (
            data["obs"],
            data["actions"],
            data["obp1s"],
            data["rewards"],
            data["dones"],
            data["discounts"],
        )

        # print("obs:", obs.shape)  # (50, 10, 160, 160, 1)
        # print("actinons", actions.shape)
        # print("obp1s:", obs.shape)  # (50, 10, 160, 160, 1)
        # print("rewards:", rewards.shape)  # (50, 10)
        # print("dones:", rewards.shape)  # (50, 10)
        # print("discounts:", discounts.shape)  # (50, 10)

        start_time = time.time()
        rewards_mean = self.model.update_dreaming(
            obs, actions, obp1s, rewards, dones, discounts
        )

        # self._step.assign_add(len(data["dones"]))

        end_time = time.time()
        # print("update time = ", end_time - start_time)
        return rewards_mean

    def reset_total_step(self):
        """
        When prefill end, call this function
        """
        self.total_step = 1


from __future__ import annotations

import asyncio
import inspect
from subprocess import PIPE
from typing import (
    Any,
    Callable,
    ClassVar,
    Coroutine,
    Dict,
    List,
    Optional,
    Tuple,
    Union,
)

import ray
import wrapt
from decorator import decorator

def check_enabled() -> bool:
    return RayRemote.ENABLED

class AbstractCallableDecorator:
    """
    An abstract class that should be subclassed to decorate different flavors of callables:

    - function
    - static method
    - class method
    - instance method
    - class with `__call__`

    Usage
    --------

    ```
    @AbstractCallableDecorator(...)
    def func(...):
        ...
    ```

    ```
    @AbstractCallableDecorator.transform(...)
    def func(...):
        ...
    ```

    ```
    @AbstractCallableDecorator.transform
    def func(...):
        ...
    ```

    """

    def _run(self, fn: Callable, *args: Any, **kwargs: Any):
        """Call the decorated function with its arguments."""
        return fn(*args, **kwargs)

    @classmethod
    def transform(cls, wrapped: Optional[Callable] = None, *args: Any, **kwargs: Any):
        """
        Create a wrapper by first create a decorator with provided arguments and then apply decorator to wrapped.
        """
        return cls(*args, **kwargs)(wrapped)

    @wrapt.decorator
    def __call__(self, wrapped, instance, args, kwargs):
        if not callable(wrapped):
            raise TypeError(f"{self.__class__.__name__} should decorate a callable")

        if instance is None:
            if inspect.isclass(wrapped):
                raise TypeError(f"{self.__class__.__name__} cannot decorate a class")
            elif inspect.isfunction(wrapped):
                return self._run(wrapped, *args, **kwargs)
            else:
                try:
                    # is callable
                    cls = getattr(wrapped, "__class__")
                    instance = wrapped
                    wrapped = cls.__call__
                except AttributeError:
                    return self._run(wrapped, *args, **kwargs)

        @decorator
        def wrapper(fn: Callable, *args: Any, **kwargs: Any):
            return fn(*args, **kwargs)

        return self._run(wrapper(wrapped), instance, *args, **kwargs)

class RayRemote(AbstractCallableDecorator):
    """
    Combining [`ray.remote`](https://docs.ray.io/en/master/package-ref.html#ray-remote) and invoke of remote function.

    Usage
    --------

    ```
    @RayRemote(...)
    def func(...):
        ...
    ```
    """

    ENABLED: ClassVar[bool] = True

    def __init__(
        self,
        wrapped: Optional[Callable] = None,
        *,
        num_returns: Optional[int] = None,
        num_cpus: Optional[float] = None,
        num_gpus: Optional[int] = None,
        resources: Optional[Dict[str, float]] = None,
        accelerator_type: Optional[Any] = None,
        max_calls: Optional[int] = None,
        max_restarts: Optional[int] = None,
        max_task_retries: Optional[int] = None,
        max_retries: Optional[int] = None,
        runtime_env: Optional[Dict[str, Any]] = None,
        retry_exceptions: Optional[bool] = None,
    ):
        """
        Args:
            num_returns (int): This is only for *remote functions*. It specifies
                the number of object refs returned by
                the remote function invocation.
            num_cpus (float): The quantity of CPU cores to reserve
                for this task or for the lifetime of the actor.
            num_gpus (int): The quantity of GPUs to reserve
                for this task or for the lifetime of the actor.
            resources (Dict[str, float]): The quantity of various custom resources
                to reserve for this task or for the lifetime of the actor.
                This is a dictionary mapping strings (resource names) to floats.
            accelerator_type: If specified, requires that the task or actor run
                on a node with the specified type of accelerator.
                See `ray.accelerators` for accelerator types.
            max_calls (int): Only for *remote functions*. This specifies the
                maximum number of times that a given worker can execute
                the given remote function before it must exit
                (this can be used to address memory leaks in third-party
                libraries or to reclaim resources that cannot easily be
                released, e.g., GPU memory that was acquired by TensorFlow).
                By default this is infinite.
            max_restarts (int): Only for *actors*. This specifies the maximum
                number of times that the actor should be restarted when it dies
                unexpectedly. The minimum valid value is 0 (default),
                which indicates that the actor doesn't need to be restarted.
                A value of -1 indicates that an actor should be restarted
                indefinitely.
            max_task_retries (int): Only for *actors*. How many times to
                retry an actor task if the task fails due to a system error,
                e.g., the actor has died. If set to -1, the system will
                retry the failed task until the task succeeds, or the actor
                has reached its max_restarts limit. If set to `n > 0`, the
                system will retry the failed task up to n times, after which the
                task will throw a `RayActorError` exception upon :obj:`ray.get`.
                Note that Python exceptions are not considered system errors
                and will not trigger retries.
            max_retries (int): Only for *remote functions*. This specifies
                the maximum number of times that the remote function
                should be rerun when the worker process executing it
                crashes unexpectedly. The minimum valid value is 0,
                the default is 4 (default), and a value of -1 indicates
                infinite retries.
            runtime_env (Dict[str, Any]): Specifies the runtime environment for
                this actor or task and its children. See
                :ref:`runtime-environments` for detailed documentation. This API is
                in beta and may change before becoming stable.
            retry_exceptions (bool): Only for *remote functions*. This specifies
                whether application-level errors should be retried
                up to max_retries times.
        """
        kwargs = {
            name: value
            for name, value in locals().items()
            if name != "self" and name != "kwargs" and name != "wrapped" and value is not None
        }

        self.__ray_wrapper = ray.remote(**kwargs) if kwargs else ray.remote

        if wrapped:
            raise ValueError(f"{self.__class__.__name__} should be initialized with arguments")

    def _run(self, fn: Callable, *args: Any, **kwargs: Any) -> ray.ObjectRef:
        try:
            ray_callable = self.__ray_wrapper(fn)
        except ValueError as error:
            if "async def" in str(error):

                @decorator
                def wrapper(fn: Callable, *args: Any, **kwargs: Any):
                    return fn(*args, **kwargs)

                ray_callable = self.__ray_wrapper(wrapper(fn))
            else:
                raise

        return ray_callable.remote(*args, **kwargs)

    def run_remote(self, fn: Callable, *args: Any, **kwargs: Any) -> ray.ObjectRef:
        """
        Run a callable in ray.

        This is short for `ray.remote(fn).remote(*args, **kwargs)`.
        """
        return self._run(fn, *args, **kwargs)

    @wrapt.decorator(enabled=check_enabled)
    def __call__(self, wrapped, instance, args, kwargs):
        return super(self.__class__, self).__call__(wrapped)(*args, **kwargs)

class AwaitCoroutine(AbstractCallableDecorator):
    """
    Convert a coroutine to a blocking synchronous operation.
    """

    def _run(self, fn, *args, **kwargs):
        return asyncio.get_event_loop().run_until_complete(fn(*args, **kwargs))

class RayAsyncActor:
    def __init__(self, coroutine: Coroutine):
        super().__init__()
        self.coroutine = coroutine

    @classmethod
    def transform(cls, wrapped: Optional[Coroutine] = None, **ray_options: Any):
        if ray_options:
            actor_constructor = ray.remote(cls).remote(**ray_options)
        else:
            actor_constructor = ray.remote(cls).remote

        if wrapped:
            return actor_constructor(wrapped)
        else:
            return actor_constructor

    async def __call__(self, *args: Any, **kwargs: Any):
        return await self.coroutine(*args, **kwargs)

class ShellActor:
    def __init__(self, *args: List[str], stdin=PIPE, stdout=PIPE, stderr=PIPE, **kwargs: Any):
        self.args = args
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr
        self.shell_kwargs = kwargs

    async def _start_async(self) -> None:
        self.process: asyncio.Process = await asyncio.create_subprocess_exec(
            *self.args,
            stdin=self.stdin,
            stdout=self.stdout,
            stderr=self.stderr,
            **self.shell_kwargs,
        )

    async def _communicate(
        self, _input: Optional[bytes] = None, encoding: Optional[str] = None
    ) -> Tuple[Union[str, bytes], Union[str, bytes]]:
        stdout, stderr = await self.process.communicate(_input)
        if encoding:
            return stdout.decode(), stderr.decode()
        else:
            return stdout, stderr

    async def run_async(
        self, _input: Optional[bytes] = None, encoding: Optional[str] = None
    ) -> Tuple[Union[str, bytes], Union[str, bytes]]:
        await self._start_async()
        return await self._communicate(_input, encoding)


import tkinter.messagebox

from solentware_grid.gui.dataedit import DataEdit

from .cqltoplevel import CQLToplevel, CQLToplevelEdit
from .topleveltext import EditText

class CQLDbEdit(EditText, DataEdit):
    """Edit ChessQL statement on database, or insert a new record.

    parent is used as the master argument in CQLToplevel calls.

    ui is used as the ui argument in CQLToplevel calls.

    newobject, parent, oldobject, and the one or two CQLToplevel instances
    created, are used as arguments in the super.__init__ call.

    showinitial determines whether a CQLToplevel is created for oldobject if
    there is one.

    Attribute text_name provides the name used in widget titles and message
    text.

    Methods _get_title_for_object and _set_item, and properties ui_base_table;
    ui_items_in_toplevels; and ui, allow similar methods in various classes
    to be expressed identically and defined once.

    """

    text_name = "ChessQL Statement"

    def __init__(
        self,
        newobject=None,
        parent=None,
        oldobject=None,
        showinitial=True,
        ui=None,
    ):
        """Extend and create toplevel to edit or insert ChessQL statement."""
        if not oldobject:
            showinitial = False
        super().__init__(
            newobject=newobject,
            parent=parent,
            oldobject=oldobject,
            newview=CQLToplevelEdit(master=parent, ui=ui),
            title="",
            oldview=CQLToplevel(master=parent, ui=ui)
            if showinitial
            else showinitial,
        )
        self._initialize()

    def _get_title_for_object(self, object_=None):
        """Return title for Toplevel containing a ChessQL statement object_.

        Default value of object_ is oldobject attribute from DataEdit class.

        """
        if object_ is None:
            object_ = self.oldobject
        if object_:
            return "  ".join(
                (
                    self.text_name.join(("Edit ", ":")),
                    object_.value.get_name_text(),
                )
            )
        return "".join(("Insert ", self.text_name))

    @property
    def ui_base_table(self):
        """Return the User Interface TagRosterGrid object."""
        return self.ui.base_partials

    @property
    def ui_items_in_toplevels(self):
        """Return the User Interface objects in Toplevels."""
        return self.ui.partials_in_toplevels

    @property
    def ui(self):
        """Return the User Interface object from 'editable' view."""
        return self.newview.ui

    @staticmethod
    def _set_item(view, object_):
        """Populate view with the CQL query extracted from object_."""
        view.cql_statement.process_statement(object_.get_srvalue())
        view.set_and_tag_item_text(reset_undo=True)

    def dialog_ok(self):
        """Return update action response (True for deleted).

        Delegate to superclass if the active item on the main display is not a
        'Position | Partial' widget and contains a valid statement.

        """
        self.newobject.value.load(
            repr(self.newview.get_name_cql_statement_text())
        )
        title = self._get_title_for_object()
        if not self.newobject.value.get_name_text():
            if not self.newobject.value.cql_error:
                tkinter.messagebox.showerror(
                    parent=self.parent,
                    title=title,
                    message="".join(
                        (
                            "The ChessQL statement has no name.\n\nPlease ",
                            "enter it's name as the first line of text.'",
                        )
                    ),
                )
            else:
                tkinter.messagebox.showerror(
                    parent=self.parent,
                    title=title,
                    message="".join(
                        (
                            "The text does not contain a valid ChessQL ",
                            "statement. ",
                        )
                    ),
                )
            return False
        if self.newobject.value.cql_error:
            if tkinter.messagebox.YES != tkinter.messagebox.askquestion(
                parent=self.parent,
                title=title,
                message="".join(
                    (
                        "Confirm request to update ChessQL statement ",
                        "named:\n\n",
                        self.newobject.value.get_name_text(),
                        "\n\non database.\n\n",
                        self.newobject.value.cql_error.get_error_report(),
                    )
                ),
            ):
                return False
        if self.ui.partial_items.active_item:
            if self.ui.partial_items.active_item.sourceobject is None:
                tkinter.messagebox.showinfo(
                    parent=self.parent,
                    title=title,
                    message="".join(
                        (
                            "Cannot use this insert dialogue while the ",
                            "active item in cql queries is one opened by ",
                            "menu action 'Position | Partial'.",
                        )
                    ),
                )
                return False
        return super().dialog_ok()

    def edit(self, commit=True):
        """Delegate to superclass to edit record then update game list."""
        if commit:
            self.datasource.dbhome.start_transaction()
        super().edit(commit=False)
        cqls = self.ui.partialpositionds(
            self.ui.base_games.datasource.dbhome,
            self.ui.base_games.datasource.dbset,
            self.ui.base_games.datasource.dbset,
            newrow=None,
        )
        cqls.update_cql_statement_games(self.newobject, commit=False)
        if commit:
            self.datasource.dbhome.commit()

    def put(self, commit=True):
        """Delegate to superclass to insert record then insert game list."""
        if commit:
            self.datasource.dbhome.start_transaction()
        super().put(commit=False)
        cqls = self.ui.partialpositionds(
            self.ui.base_games.datasource.dbhome,
            self.ui.base_games.datasource.dbset,
            self.ui.base_games.datasource.dbset,
            newrow=None,
        )
        cqls.update_cql_statement_games(self.newobject, commit=False)
        if commit:
            self.datasource.dbhome.commit()


"""Radiance Cone.

http://radsite.lbl.gov/radiance/refer/ray.html#Cone
"""
from .geometrybase import Geometry
import honeybee.typing as typing

class Cone(Geometry):
    """Radiance Cone.

    A cone is a megaphone-shaped object. It is truncated by two planes perpendicular to
    its axis, and one of its ends may come to a point. It is given as two axis endpoints,
    and the starting and ending radii:

    .. code-block:: shell

        mod cone id
        0
        0
        8
                x0      y0      z0
                x1      y1      z1
                r0      r1

    Args:
        identifier: Text string for a unique Geometry ID. Must not contain spaces
            or special characters. This will be used to identify the object across
            a model and in the exported Radiance files.
        center_pt_start: Cone start center point as (x, y, z) (Default: (0, 0 ,0)).
        radius_start: Cone start radius as a number (Default: 10).
        center_pt_end: Cone end center point as (x, y, z) (Default: (0, 0 ,10)).
        radius_end: Cone end radius as a number (Default: 0).
        modifier: Geometry modifier (Default: None).
        dependencies: A list of primitives that this primitive depends on. This
            argument is only useful for defining advanced primitives where the
            primitive is defined based on other primitives. (Default: [])

    Properties:
        * identifier
        * display_name
        * center_pt_start
        * radius_start
        * center_pt_end
        * radius_end
        * values
        * modifier
        * dependencies

    """
    __slots__ = ('_center_pt_start', '_radius_start', '_center_pt_end', '_radius_end')

    def __init__(self, identifier, center_pt_start=None, radius_start=10,
                 center_pt_end=None, radius_end=0, modifier=None, dependencies=None):
        """Radiance Cone."""
        Geometry.__init__(self, identifier, modifier=modifier, dependencies=dependencies)
        self.center_pt_start = center_pt_start or (0, 0, 0)
        self.radius_start = radius_start if radius_start is not None else 10
        self.center_pt_end = center_pt_end or (0, 0, 10)
        self.radius_end = radius_end if radius_end is not None else 0
        self._update_values()

    def _update_values(self):
        """update value dictionaries."""
        self._values[2] = \
            [self.center_pt_start[0], self.center_pt_start[1], self.center_pt_start[2],
             self.center_pt_end[0], self.center_pt_end[1], self.center_pt_end[2],
             self.radius_start, self.radius_end]

    @property
    def center_pt_start(self):
        """Cone start center point as (x, y, z). Default is (0, 0 ,0)."""
        return self._center_pt_start

    @center_pt_start.setter
    def center_pt_start(self, value):
        self._center_pt_start = tuple(float(v) for v in value)
        assert len(self._center_pt_start) == 3, \
            'Radiance Cone center point must have 3 values for (x, y, z).'

    @property
    def radius_start(self):
        """Cone start radius as a number. Default is 10."""
        return self._radius_start

    @radius_start.setter
    def radius_start(self, value):
        self._radius_start = typing.float_positive(value)

    @property
    def center_pt_end(self):
        """Cone end center point as (x, y, z). Default is (0, 0 ,10)."""
        return self._center_pt_end

    @center_pt_end.setter
    def center_pt_end(self, value):
        self._center_pt_end = tuple(float(v) for v in value)
        assert len(self._center_pt_end) == 3, \
            'Radiance Cone center point must have 3 values for (x, y, z).'

    @property
    def radius_end(self):
        """ Cone end radius as a number. Default is 0."""
        return self._radius_end

    @radius_end.setter
    def radius_end(self, value):
        self._radius_end = typing.float_positive(value)

    @classmethod
    def from_primitive_dict(cls, primitive_dict):
        """Initialize a Cone from a primitive dict.

        Args:
            data: A dictionary in the format below.

        .. code-block:: python

            {
            "modifier": {},  # primitive modifier (Default: None)
            "type": "cone",  # primitive type
            "identifier": "",  # primitive identifier
            "display_name": "",  # primitive display name
            "values": [],  # values
            "dependencies": []
            }
        """
        assert 'type' in primitive_dict, 'Input dictionary is missing "type".'
        if primitive_dict['type'] != cls.__name__.lower():
            raise ValueError(
                'Type must be %s not %s.' % (cls.__name__.lower(), primitive_dict['type'])
            )

        modifier, dependencies = cls.filter_dict_input(primitive_dict)
        values = primitive_dict['values'][2]

        cls_ = cls(
            identifier=primitive_dict['identifier'],
            center_pt_start=values[0:3],
            radius_start=values[6],
            center_pt_end=values[3:6],
            radius_end=values[7],
            modifier=modifier,
            dependencies=dependencies
        )
        if 'display_name' in primitive_dict and primitive_dict['display_name'] is not None:
            cls_.display_name = primitive_dict['display_name']

        # this might look redundant but it is NOT. see glass for explanation.
        cls_.values = primitive_dict['values']
        return cls_

    @classmethod
    def from_dict(cls, data):
        """Initialize a Cone from a dictionary.

        Args:
            data: A dictionary in the format below.

        .. code-block:: python

            {
            "type": "cone",  # Geometry type
            "modifier": {} ,
            "identifier": "",  # Geometry identifer
            "display_name": "",  # Geometry display name
            "center_pt_start": (0, 0, 0),
            "radius_start": float,
            "center_pt_end": (0, 0, 10),
            "radius_end": float,
            "dependencies": []
            }
        """
        assert 'type' in data, 'Input dictionary is missing "type".'
        if data['type'] != cls.__name__.lower():
            raise ValueError(
                'Type must be %s not %s.' % (cls.__name__.lower(),
                    data['type'])
            )
        modifier, dependencies = cls.filter_dict_input(data)

        new_obj = cls(identifier=data["identifier"],
                      center_pt_start=(data["center_pt_start"]),
                      radius_start=data["radius_start"],
                      center_pt_end=( data["center_pt_end"]),
                      radius_end=data["radius_end"],
                      modifier=modifier,
                      dependencies=dependencies)
        if 'display_name' in data and data['display_name'] is not None:
            new_obj.display_name = data['display_name']
        return new_obj

    def to_dict(self):
        """Translate this object to a dictionary."""
        base = {
            "modifier": self.modifier.to_dict(),
            "type": self.__class__.__name__.lower(),
            "identifier": self.identifier,
            "radius_start": self.radius_start,
            "center_pt_start": self.center_pt_start,
            "radius_end": self.radius_end,
            "center_pt_end": self.center_pt_end,
            'dependencies': [dp.to_dict() for dp in self.dependencies]
        }
        if self._display_name is not None:
            base['display_name'] = self.display_name
        return base

    def __copy__(self):
        mod, depend = self._dup_mod_and_depend()
        new_obj = self.__class__(
            self.identifier, self.center_pt_start, self.radius_start, self.center_pt_end,
            self.radius_end, mod, depend)
        new_obj._display_name = self._display_name
        return new_obj


T = int(input())
for _t in range(1, T+1):
    N, P = [int(x) for x in input().split()]
    X = []
    for i in range(P):
        x = input()
        flag = False
        for j in range(len(X)):
            if x in X[j] and X[j].index(x) == 0:
                X[j] = x
                flag = True
                break
            if X[j] in x and x.index(X[j]) == 0:
                flag = True
                break
        if not flag:
            X.append(x)
    ret = 2 ** N
    for x in X:
        ret -= 2 ** (N - len(x))
    print('Case #{}: {}'.format(_t, ret))


from flask import Flask, request, jsonify
from flask_babel import Babel
from flask_cors import cross_origin

app = Flask(__name__)
babel = Babel(app)
supported = app.config.get('BABEL_SUPPORTED_LOCALES', ['en', 'ru', 'pt'])
default = app.config.get('BABEL_DEFAULT_LOCALE', 'en')

motto = {'en': 'Semantyca of your business', 'ru': 'Семантика вашего бизнеса', 'pt': 'Semantyca do seu negócio'}
subMotto = {'en': 'This is an inception. All you get is this web site', 'ru': 'Это начало. Все, что у нас есть, это этот сайт', 'pt': 'Este é um começo. Tudo que nos temos é este site'}
homeButton = {'en': 'Home', 'ru': 'Домой', 'pt': 'Para casa'}
tasksButton = {'en': 'Tasks', 'ru': 'Задачи', 'pt': 'Tarefas'}

@babel.localeselector
def get_locale():
    locale = request.accept_languages.best_match(supported, default)
    return 'pt'

@app.route('/home', methods=["GET"])
@cross_origin(origin='*')
def change_lang_default():
    return change_lang('en')

@app.route('/home/<lang>', methods=["GET"])
@cross_origin(origin='*')
def change_lang(lang):
    lang == request.view_args['lang']
    page = {
        'lang': lang,
        'name': 'Semantyca',
        'motto': motto.get(lang),
        'subMotto':  subMotto.get(lang),
        'menuHome':  homeButton.get(lang),
        'menuTasks': tasksButton.get(lang)

    }
    return jsonify(page)

@app.route('/login', methods=["POST"])
@cross_origin(origin='*')
def login():
    return 'Hello login!'

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


#The City of Chicago database a csv of hospitals and their locations within
#the city, but no information on capacity (number of beds). The UIC School of
#Public Health has a web table with hospital names and bed capacities, but no
#locations. This file seeks to match these two datasets to obtain one table
#of with hospital names, locations, and capacity. Then, we group by zip code
#to find the number of hospital beds per zip code.
#
#Used to make hospital_beds_by_zip.csv
#
#Sources used:
#    --forgot how strip worked for strings, so I'm citing that here
#        https://www.w3schools.com/python/ref_string_strip.asp
#    --see README in csv_files section for data sources used

import pandas as pd
import jellyfish
import bs4
import requests
import util

def get_hospital_capacities(url):
    '''
    Function that takes in the url containing a table of hospitals in Chicago
    and their bed capacities, and returns a dataframe

    Inputs:
        url (string): the url of the website containing the hospital bed info
            (https://publichealth.uic.edu/uic-covid-19-public-health-response/
             covid-19-maps-chicago-illinois/number-of-hospital-beds-in-chicago/)

    Outputs:
        capacity_df (Pandas DataFrame) dataframe containing hospitals,
            number of ICU beds in the hospitals, and number of medical
            or surgical beds in the hospital
    '''

    request = util.get_request(url)
    if request == None:
        return 'Request Failed'

    soup = bs4.BeautifulSoup(request.text, 'html5lib')

    #Extract information from the table on the site
    col_list = soup.find_all('th', scope = 'col')
    hosp_list = soup.find_all('th', scope = 'row')
    numbers_list = soup.find_all('td')

    hosp_to_medbed = {}
    hosp_to_icubed = {}

    for i, hosp in enumerate(hosp_list):

        #Skip the last two rows of the site table, contains totals
        #for city and state, which are not used in our analysis
        if i == len(hosp_list) - 2:
            break

        hosp_to_medbed[hosp.text.strip()] = int(numbers_list[2*i].text.strip())
        hosp_to_icubed[hosp.text.strip()] = int(numbers_list[1 + 2*i].text.strip())

    capacity_df = pd.DataFrame([hosp_to_medbed, hosp_to_icubed]).T.reset_index()

    capacity_df.rename(columns = {'index':col_list[0].text.strip(),
        0:col_list[1].text.strip(),
        1:col_list[2].text.strip()}, inplace = True)

    return capacity_df

def get_city_df(filepath):
    '''
    Function that takes in a filepath and returns the dataframe of hospitals from
    the City of Chicago Data Portal. This data does not include hospital capacities
    so we must link with the data scraped in the previous functions to match
    hospital capacities and their locations.

    Inputs:
        filepath (string): the filepath where csv from city database is

    Outputs:
        city_df (pandas DataFrame): DataFrame containing hospital names and location
            information
    '''

    city_df = pd.read_csv(filepath)
    city_df = city_df[['COMMONNAME,C,254', 'CITY,C,254', 'ADDRESS,C,254', 'ZIP,C,5']]
    col_rename = {'COMMONNAME,C,254':'Hospital', 'CITY,C,254':'City', \
            'ADDRESS,C,254':'Address', 'ZIP,C,5':'Zip_Code'}

    city_df.rename(columns = col_rename, inplace = True)

    return city_df

def match_with_city_csv(capacity_df, city_df):
    '''
    Function that does sort of a pseudo-record linkage to match the hospitals
    from UIC data (hospital bed data) with the hospitals in the city csv. Uses
    Jaro-Winkler scores to determine similarity between the hospital names
    in both sets, and if they are very high, we classify them as a match
    and match them in a resulting dataset. If they are so-so, we bin into
    a possible dataset and examine by eye/google search.

    Inputs:
        capacity_df (Pandas DataFrame): Pandas DataFrame of UIC web data
        city_df (Pandas DataFrame): Pandas DataFrame of City Data Portal data

    Outputs:
        matches, maybe, nope (Pandas DataFrames): DataFrames with columns
            in capacity_df and city_df, where each row is a pairing between
            rows of city_df and capacity_df. matches have high J-W scores
            between names, maybe have sort of high vales, nope have low
            values and are not worth sifting through
    '''

    #ci stands for 'city', ca stands for 'capacity'
    match_inds_ci = []
    match_inds_ca = []
    maybe_inds_ci = []
    maybe_inds_ca = []
    nope_inds_ci = []
    nope_inds_ca = []

    for i, hosp1 in city_df.iterrows():
        for j, hosp2 in capacity_df.iterrows():
            #Wanted to take out the word Hospital from the names, might give
            #more true matches
            score = jellyfish.jaro_winkler(hosp1['Hospital'].replace('Hospital', ''), \
                    hosp2['Hospital'].replace('Hospital', ''))

            if score >= 0.90:
                match_inds_ci.append(i)
                match_inds_ca.append(j)

            elif score >= 0.65 and score < 0.90:
                #dont include pairings in the maybe file where one hospital in
                #the pair has already been matched
                if i not in match_inds_ci and j not in match_inds_ca:
                    maybe_inds_ci.append(i)
                    maybe_inds_ca.append(j)

            else:
                if i not in match_inds_ci and j not in match_inds_ca:

                    nope_inds_ci.append(i)
                    nope_inds_ca.append(j)

    matches = pd.concat([city_df.iloc[match_inds_ci].reset_index(drop = True), \
            capacity_df.iloc[match_inds_ca].reset_index(drop = True)], axis = 1)
    maybe = pd.concat([city_df.iloc[maybe_inds_ci].reset_index(drop = True), \
            capacity_df.iloc[maybe_inds_ca].reset_index(drop = True)], axis = 1)
    nope = pd.concat([city_df.iloc[nope_inds_ci].reset_index(drop = True), \
            capacity_df.iloc[nope_inds_ca].reset_index(drop = True)], axis = 1)

    return matches, maybe, nope

#After eyeballing the matches and maybe matches data, it seemed like
#all the matches were correct, and the following indices of the maybe
#matches were also correct. It seems like there is a significant disconnect
#between our Chicago hospital data and the bed info we found (significant
#lack of overlap with hospitals included), some hospitals no longer exist,
#and some names can be different to the point where it is impossible to tell
#if they are the same without physically googling and matching addresses.

#combinations of hospitals in the maybe dataframe that I googled and for sure
#can say are actually matches
MATCHES_MAYBE = [4, 6, 7, 8, 9, 10, 12, 16, 21, 23, 38, 35]

def add_matches_maybe(url, citydf_filepath):
    '''
    Function to take the matches dataframe from the previous function and
    tack on the rows from maybe where I physically looked at it and could
    tell that these rows were actually matches.

    Inputs:

        url (string): the UIC url (above)
        citydf_filepath (string): the filepath to find the Hospitals.csv
            file from the city database

    Outputs:

        city_df (Pandas DataFrame): Hospitals.csv data in DataFrame format
        capacity_df (Pandas DataFrame): UIC web table data in DataFrame format
        combined_df (Pandas DataFrame): DataFrame whose rows are known matches
            between the two datasets
    '''

    capacity_df = get_hospital_capacities(url)
    city_df = get_city_df(citydf_filepath)
    matches, maybe, nope = match_with_city_csv(capacity_df, city_df)
    matches_from_maybe = maybe.iloc[MATCHES_MAYBE]
    combined_df = pd.concat([matches, matches_from_maybe]).reset_index(drop = True)

    return city_df, capacity_df, combined_df

#Now, after looking at the combined_df, I tried to match the rest of the
#hospitals from the city_df and capacity_df not included in combined_df
#by googling the names of the hospitals and in each frame and physically
#matching them in this way so as to achieve the most complete data.
#It should be noted that some hospitals in the city database were
#addiction centers/mental health clinics, so it makes sense that these
#would not be included in the UIC hospital bed data.

#indices from city and capacity dataframes that match
HANDPICKED_INDS_CI = [14, 24, 25, 29, 30, 34]
HANDPICKED_INDS_CA = [24, 10, 18, 15, 14, 1]

def make_final_matches(url, citydf_filepath):
    '''
    Function that takes the output of the last function (an incomplete set of matches)
    and returns a dataframe with rows added from the known matches I picked out
    by googling (given by HANDPICKED_INDS_CI and HANDPICKED_INDS_CA)

    Inputs:

        url (string): UIC url
        citydf_filepath(string): the filepath to find the Hospitals.csv
            file from the city database

    Outputs:

        final_df (Pandas DataFrame): DataFrame with number of ICU beds and
            surgical/medical beds per zip code
    '''

    city_df, capacity_df, record_linkage_df = add_matches_maybe(url, citydf_filepath)

    handpicked_rows_ci = city_df.iloc[HANDPICKED_INDS_CI].reset_index(drop = True)
    handpicked_rows_ca = capacity_df.iloc[HANDPICKED_INDS_CA].reset_index(drop = True)

    handpicked_rows = pd.concat([handpicked_rows_ci, handpicked_rows_ca], axis = 1)
    final_df = pd.concat([record_linkage_df, handpicked_rows]).reset_index(drop = True)

    return final_df

def get_beds_by_zip(url, citydf_filepath):
    '''
    The grandest function of them all: computes the output from the last function,
    and takes this most complete data table of hospitals with locations and
    capacities and returns a DataFrame with number of beds for each zip code.

    '''

    hosp_bed_df = make_final_matches(url, citydf_filepath)

    beds_to_zip_df = hosp_bed_df.groupby('Zip_Code').agg({'ICU Beds' : sum, 'Medical and Surgical Beds' : sum}).reset_index()

    return beds_to_zip_df

bed_info_url = 'https://publichealth.uic.edu/uic-covid-19-public-health-response/covid-19-maps-chicago-illinois/number-of-hospital-beds-in-chicago/'
city_df_filepath = '../csv_files/source_csv_files/Hospitals.csv'

get_beds_by_zip(bed_info_url, city_df_filepath).to_csv('../csv_files/hospital_beds_by_zip.csv', index = False)


import pandas_datareader as pdr
import numpy as np
from threading import Thread

# Creating a base class
class CpiData:
    def __init__(self):

        self.cpi = []

        # Countries and associated symbols
        self.countries = {
        'UNITED STATES' : 'ticker=CPIUS',
        'CHINA' : 'ticker=CPICN',
        'JAPAN' : 'ticker=CPIJP',
        'GERMANY' : 'ticker=CPIDE',
        'FRANCE' : 'ticker=CPIFR',
        'INDIA' : 'ticker=CPIIN',
        'ITALY' : 'ticker=CPIIT',
        'BRAZIL' : 'ticker=CPIBR',
        'SOUTH KOREA' : 'ticker=CPIKR',
        'CANADA' : 'ticker=CPICA',
        'RUSSIAN FEDERATION' : 'ticker=CPIRU',
        'SPAIN' : 'ticker=CPIES',
        'MEXICO' : 'ticker=CPIMX',
        'INDONESIA' : 'ticker=CPIID',
        'NETHERLANDS' : 'ticker=CPINL',
        'SAUDI ARABIA' : 'ticker=CPISA',
        'TURKEY' : 'ticker=CPITR',
        'SWITZERLAND' : 'ticker=CPICH',
        'TAIWAN' : 'ticker=CPITW',
        'POLAND' : 'ticker=CPIPL',
        'SWEDEN' : 'ticker=CPISE',
        'BELGIUM' : 'ticker=CPIBE',
        'ARGENTINA' : 'ticker=CPIAR',
        'THAILAND' : 'ticker=CPITH',
        'AUSTRIA' : 'ticker=CPIAT',
        'IRAN' : 'ticker=CPIIR',
        'NORWAY' : 'ticker=CPINO',
        'UNITED ARAB EMIRATES' : 'ticker=CPIAE',
        'NIGERIA' : 'ticker=CPING',
        'IRELAND' : 'ticker=CPIIE',
        'ISRAEL' : 'ticker=CPIIL',
        'SOUTH AFRICA' : 'ticker=CPIZA',
        'SINGAPORE' : 'ticker=CPISG',
        'HONG KONG' : 'ticker=CPIHK',
        'MALAYSIA' : 'ticker=CPIMY',
        'DENMARK' : 'ticker=CPIDK',
        'COLOMBIA' : 'ticker=CPICO',
        'PHILIPPINES' : 'ticker=CPIPH',
        'PAKISTAN' : 'ticker=CPIPK',
        'CHILE' : 'ticker=CPICL',
        'BANGLADESH' : 'ticker=CPIBD',
        'FINLAND' : 'ticker=CPIFI',
        'EGYPT' : 'ticker=CPIEG',
        'CZECH REPUBLIC' : 'ticker=CPICZ',
        'VIETNAM' : 'ticker=CPIVN',
        'PORTUGAL' : 'ticker=CPIPT',
        'ROMANIA' : 'ticker=CPIRO',
        'PERU' : 'ticker=CPIPE',
        'IRAQ' : 'ticker=CPIIQ',
        'GREECE' : 'ticker=CPIGR',
        'QATAR' : 'ticker=CPIQA',
        'ALGERIA' : 'ticker=CPIDZ',
        'KAZAKHSTAN' : 'ticker=CPIKZ',
        'HUNGARY' : 'ticker=CPIHU',
        'KUWAIT' : 'ticker=CPIKW',
        'UKRAINE' : 'ticker=CPIUA',
        'MOROCCO' : 'ticker=CPIMA',
        'ECUADOR' : 'ticker=CPIEC',
        'SLOVAKIA' : 'ticker=CPISK',
        'ANGOLA' : 'ticker=CPIAO',
        'VENEZUELA' : 'ticker=CPIVE',
        'SRI LANKA' : 'ticker=CPILK',
        'KENYA' : 'ticker=CPIKE',
        'DOMINICAN REPUBLIC' : 'ticker=CPIDO',
        'ETHIOPIA' : 'ticker=CPIET',
        'OMAN' : 'ticker=CPIOM',
        'GUATEMALA' : 'ticker=CPIGT',
        'LUXEMBOURG' : 'ticker=CPILU',
        'MYANMAR' : 'ticker=CPIMM',
        'GHANA' : 'ticker=CPIGH',
        'BULGARIA' : 'ticker=CPIBG',
        'PANAMA' : 'ticker=CPIPA',
        'CROATIA' : 'ticker=CPIHR',
        'URUGUAY' : 'ticker=CPIUY',
        'BELARUS' : 'ticker=CPIBY',
        'TANZANIA' : 'ticker=CPITZ',
        'LEBANON' : 'ticker=CPILB',
        'MACAO' : 'ticker=CPIMO',
        'SLOVENIA' : 'ticker=CPISI',
        'LITHUANIA' : 'ticker=CPILT',
        'UNITED KINGDOM' : 'ticker=CPIUK'
    }

    # Fetching data from Econdb API and returning current inflation pace
    def get_cpi_all(self, ticker):
        data = pdr.DataReader(ticker, 'econdb')
        data = data.values

        # Comparing latest reading with 1 year average
        if np.mean(data[-12:]) < data[-1]:
            return 'ABOVE ITS 12-MONTH AVERAGE INDICATING INFLATION'
        else:
            return 'BELOW ITS 12-MONTH AVERAGE INDICATING DEFLATION'

    def get_cpi(self, symbol):
        g5_countries  = {
                'ticker=CPICN' : 'CHINA',
                'ticker=CPIDE' : 'GERMANY',
                'ticker=CPIIN' : 'INDIA',
                'ticker=CPIJP' : 'JAPAN',
                'ticker=CPIUS' : 'UNITED STATES'
            }
        data = pdr.DataReader(symbol, 'econdb').values
        if np.mean(data[-12:]) < data[-1]:
            result = ['INFLATION', g5_countries[symbol]]
        else:
            result = ['DEFLATION', g5_countries[symbol]]
        if result not in self.cpi:
            self.cpi.append(result)
            self.cpi.sort(key=lambda x: x[1])

    # Creating individual thread for each call to Econdb API
    def get_cpi_g5(self):

        threads = []
        tickers = ['ticker=CPICN', 'ticker=CPIDE', 'ticker=CPIIN', 'ticker=CPIJP', 'ticker=CPIUS']

        for ticker in tickers:
            thread = Thread(target=self.get_cpi, args=[ticker])
            threads.append(thread)

        for thread in threads:
            thread.start()

        for thread in threads:
            thread.join()

        return self.cpi


# coding: utf-8
import contextlib
import inspect
import logging
import os
import sys

from ._version import get_versions
__version__ = get_versions()['version']
del get_versions

@contextlib.contextmanager
def logging_restore(clear_handlers=False):
    '''
    Save logging state upon entering context and restore upon leaving.

    Parameters
    ----------
    clear_handlers : bool, optional
        If ``True``, clear active logging handlers while within context.

    Example
    -------

    Set logging level to ``DEBUG``, set logging level to ``INFO`` within
    context, and verify logging level is restored to ``DEBUG`` upon exiting
    context.

    >>> import logging_helpers as lh
    >>> import logging
    >>> logging.basicConfig(level=logging.DEBUG)
    >>> logging.debug('hello, world!')
    DEBUG:root:hello, world!
    >>> with lh.logging_restore():
    ...     logging.root.setLevel(logging.INFO)
    ...
    >>> logging.debug('hello, world!')
    DEBUG:root:hello, world!

    '''
    handlers = logging.root.handlers[:]
    level = logging.root.getEffectiveLevel()
    if clear_handlers:
        for h in handlers:
            logging.root.removeHandler(h)
    yield
    handlers_to_remove = logging.root.handlers[:]
    [logging.root.removeHandler(h) for h in handlers_to_remove]
    [logging.root.addHandler(h) for h in handlers]
    logging.root.setLevel(level)

def _L(skip=0):
    '''Shorthand to get logger for current function frame.'''
    return logging.getLogger(caller_name(skip + 1))

# Public Domain, i.e. feel free to copy/paste
# Considered a hack in Python 2
#
# Ported from [here][1].
# See [here][2] for modifications.
#
# [1]: https://gist.github.com/techtonik/2151727
# [2]: https://gist.github.com/techtonik/2151727#gistcomment-2333747
def caller_name(skip=2):
    """
    Get a name of a caller in the format module.class.method

    `skip` specifies how many levels of stack to skip while getting caller
    name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.

    An empty string is returned if skipped levels exceed stack height

    .. versionchanged:: v0.4
        Look up the module name from the frame globals dictionary rather than
        by calling :func:`inspect.getmodule()`.  Calling
        :func:`inspect.getmodule()` is ~1000x slower compared to looking up the
        module name from the globals dictionary (~80 µs vs 90 ns).
    """
    def stack_(frame):
        framelist = []
        while frame:
            framelist.append(frame)
            frame = frame.f_back
        return framelist

    stack = stack_(sys._getframe(1))
    start = 0 + skip
    if len(stack) < start + 1:
        return ''
    parentframe = stack[start]

    # Look up module name from globals dictionary in frame rather than by
    # calling `inspect.getmodule()`.  Calling `inspect.getmodule()` is ~1000x
    # slower compared to looking up the module name from the globals dictionary
    # (~80 µs vs 90 ns).
    module_name = parentframe.f_globals['__name__']
    name = [module_name]

    # detect classname
    if 'self' in parentframe.f_locals:
        # I don't know any way to detect call from the object method
        # XXX: there seems to be no way to detect static method call - it will
        #      be just a function call
        name.append(parentframe.f_locals['self'].__class__.__name__)
    codename = parentframe.f_code.co_name
    if codename != '<module>':  # top level usually
        name.append(codename)  # function or a method
    return ".".join(name)


import tweepy
import pandas as pd
import preprocessor as p
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score, confusion_matrix
import pickle

# Insert your Twitter API keys here
consumer_key = "YOUR_CONSUMER_KEY"
consumer_secret = "YOUR_CONSUMER_SECRET"
access_token = "YOUR_ACCESS_TOKEN"
access_token_secret = "YOUR_ACCESS_TOKEN_SECRET"

# Authenticate with the Twitter API
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)

# Create an API object
api = tweepy.API(auth)

# Retrieve tweets containing a specific keyword
tweets = api.search(q="keyword", lang="en", count=100)

# Convert the tweets to a list of strings
tweet_texts = [tweet.text for tweet in tweets]

# Preprocess the data by cleaning it
cleaned_tweet_texts = []
for tweet in tweet_texts:
    cleaned_tweet = p.clean(tweet)
    cleaned_tweet_texts.append(cleaned_tweet)

# Convert the text to a numerical format using TfidfVectorizer
vectorizer = TfidfVectorizer()
tweet_vectors = vectorizer.fit_transform(cleaned_tweet_texts)

# Split the dataset into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(tweet_vectors, labels, test_size=0.2)

# Train the neural network using the training set
classifier = MultinomialNB()
classifier.fit(X_train, y_train)

# Evaluate the neural network on the testing set
y_pred = classifier.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy)

# Serialize the model and save it to disk
with open('spam_detection_model.pkl', 'wb') as f:
    pickle.dump(classifier, f)

# Use the trained model to classify new tweets as spam or not spam
new_tweet = "This is a new tweet"
cleaned_new_tweet = p.clean(new_tweet)
new_tweet_vector = vectorizer.transform([cleaned_new_tweet])
new_tweet_label = classifier.predict(new_tweet_vector)
print("New tweet label:", new_tweet_label)

# Count the number of tweets that were detected as spam
spam_count = sum(y_pred)

# Count the number of tweets that were not detected as spam
not_spam_count = len(y_pred) - spam_count

# Create a bar chart
plt.bar(['Spam', 'Not Spam'], [spam_count, not_spam_count])
plt.ylabel('Number of Tweets')
plt.show()

# Save the bar chart to a file
plt.savefig('spam_count.png')


import argparse
import os
import sys
from tqdm import tqdm
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
import torch
from utils.load_model import load_model
from data import data_loaders

CIFAR100_TRAIN_MEAN = (0.5070751592371323, 0.48654887331495095, 0.4409178433670343)
CIFAR100_TRAIN_STD = (0.2673342858792401, 0.2564384629170883, 0.27615047132568404)
if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument('--net', type=str, required=True, help='net type')
    parser.add_argument('--weights', type=str, required=True, help='the weights file you want to test')
    parser.add_argument('--batch_size', type=int, default=128, help='batch size for dataloader')

    args = parser.parse_args()
    net_name = args.net
    batch_size = args.batch_size
    weights = args.weights

    net = load_model(net_name)

    cifar100_test_loader = data_loaders.testing_data_loader(CIFAR100_TRAIN_MEAN, CIFAR100_TRAIN_STD,
                                                            batch_size=batch_size, num_workers=4, shuffle=True)

    net.load_state_dict(torch.load(args.weights))
    print(net)
    net.eval()

    correct_1 = 0.0
    correct_5 = 0.0
    total = 0

    with torch.no_grad():
        for n_iter, (image, label) in tqdm(enumerate(cifar100_test_loader)):
            output = net(image)
            _, pred = output.topk(5, 1, largest=True, sorted=True)

            label = label.view(label.size(0), -1).expand_as(pred)
            correct = pred.eq(label).float()

            # compute top 5
            correct_5 += correct[:, :5].sum()

            # compute top1
            correct_1 += correct[:, :1].sum()

    print()
    print("Top 1 err: ", 1 - correct_1 / len(cifar100_test_loader.dataset))
    print("Top 5 err: ", 1 - correct_5 / len(cifar100_test_loader.dataset))
    print("Parameter numbers: {}".format(sum(p.numel() for p in net.parameters())))


__all__ = ['compensation_data_template', 'parse_compensation_text', 'merge_compensation_data']

import re
from functools import reduce

from jvapp.models.currency import currencies, currency_lookup
from jvapp.utils.sanitize import sanitize_html

# Example -- Minimum: $157,300.00 Maximum: $344,200.00
CURRENCY_RANGE_MAX_CHAR_COUNT = 10

currency_characters = u'[$¢£¤¥֏؋৲৳৻૱௹฿៛\u20a0-\u20bd\ua838\ufdfc\ufe69\uff04\uffe0\uffe1\uffe5\uffe6]'
compensation_data_template = {
    'salary_currency': None,
    'salary_floor': None,
    'salary_ceiling': None,
    'salary_interval': None
}

def parse_compensation_text(text, salary_interval='year'):
    if not text:
        return {**compensation_data_template}
    text = sanitize_html(text)
    currency_pattern = reduce(lambda full_pattern, currency: f'{full_pattern}{"|" if full_pattern else ""}{currency}', currencies, '')
    currency_match = re.search(f'({currency_pattern})\s', text)
    bad_salary_floor = 40000 if salary_interval == 'year' else 1
    bad_salary_ceiling = 500000
    grouped_compensation_matches = get_grouped_compensation_matches(text, bad_salary_floor, bad_salary_ceiling)
    best_compensation_match = get_best_compensation_group(grouped_compensation_matches, bad_salary_floor, bad_salary_ceiling)
    currency = None
    if best_compensation_match and best_compensation_match['currency']:
        currency = currency_lookup.get(best_compensation_match['currency'], None)
    elif currency_match:
        currency = currency_match.group(0).strip()
    if not all((best_compensation_match, currency)):
        return {**compensation_data_template}

    return {
        'salary_interval': salary_interval,
        'salary_currency': currency,
        'salary_floor': best_compensation_match['salary'][0],
        'salary_ceiling': best_compensation_match['salary'][-1]
    }

def get_best_compensation_group(compensation_groups, bad_salary_floor, bad_salary_ceiling):
    possible_matches = [
        g for g in compensation_groups
        if g['salary'][0] > bad_salary_floor and g['salary'][-1] < bad_salary_ceiling
    ]
    possible_matches.sort(key=lambda x: (bool(x['currency']), len(x['salary'])), reverse=True)
    return possible_matches[0] if possible_matches else None

def get_salary_for_group(group):
    if len(group) == 1:
        return {
            'salary': [get_salary_from_match(group[0])],
            'group': group,
            'currency': group[0].group('currency')
        }
    else:
        has_thousand_marker = bool(len([g for g in group if g.group('thousand_marker')]))
        return {
            'salary': [get_salary_from_match(g, has_thousand_marker=has_thousand_marker) for g in group],
            'group': group,
            'currency': next((g.group('currency') for g in group if g.group('currency')), None)
        }

def is_good_salary_range(salary_group_data, min_salary, max_salary):
    salary = salary_group_data['salary']
    group = salary_group_data['group']
    if len(salary) == 1:
        is_good_salary = salary[0] <= max_salary and salary[0] >= min_salary
        if not is_good_salary:
            group = []
        return is_good_salary, group

    if salary[1] < salary[0]:
        return False, group[1:]
    is_good_min_salary = salary[0] <= max_salary and salary[0] >= min_salary
    is_good_max_salary = salary[1] <= max_salary and salary[1] >= min_salary
    if (not is_good_min_salary) and (not is_good_max_salary):
        return False, []
    if not is_good_min_salary:
        return False, group[1:]
    if not is_good_max_salary:
        return False, group[:1]
    return True, group

def get_grouped_compensation_matches(text, min_salary, max_salary):
    compensation_pattern = f'(?P<currency>{currency_characters})?\s?(?P<first_numbers>[0-9]+((\.?[0-9]+)|([0-9]*?))),?(?P<second_numbers>[0-9]+(\.?[0-9]+)?)?\s?(?P<thousand_marker>[kK])?'
    compensation_matches = list(re.finditer(compensation_pattern, text))
    groups = []
    combined_group = []
    last_end_position = 0
    for idx, match in enumerate(compensation_matches):
        if idx != 0 and (len(combined_group) == 2 or match.start() - CURRENCY_RANGE_MAX_CHAR_COUNT > last_end_position):
            salary = get_salary_for_group(combined_group)
            is_good_salary, updated_group = is_good_salary_range(salary, min_salary, max_salary)
            if is_good_salary:
                groups.append(combined_group)
                combined_group = [match]
            else:
                combined_group = updated_group
                combined_group.append(match)
        else:
            combined_group.append(match)
        if idx == len(compensation_matches) - 1:
            groups.append(combined_group)
        last_end_position = match.end()

    salary_groups = []
    for group in groups:
        if not group:
            continue
        salary_groups.append(get_salary_for_group(group))

    return salary_groups

def get_salary_from_match(match, has_thousand_marker=False):
    try:
        salary = float(match.group('first_numbers') + (match.group('second_numbers') or ''))
    except ValueError:
        return 0
    if match.group('thousand_marker') or has_thousand_marker:
        salary *= 1000
    return salary

def merge_compensation_data(compensation_data_sets: list):
    """Merge compensation data with preference for later data sets
    """
    compensation_data = {**compensation_data_template}
    for data_set in compensation_data_sets:
        for compensation_key in ('salary_currency', 'salary_floor', 'salary_ceiling', 'salary_interval'):
            val = data_set.get(compensation_key)
            if not val:
                continue
            compensation_data[compensation_key] = val

    return compensation_data


import time
from tkinter import filedialog
from tkinter import *
from tkinter import Frame, Entry, Tk, Listbox
import pygame
from pygame.locals import *
from mutagen.mp3 import MP3
from tkinter import messagebox
import threading
import winsound
import os
import webbrowser

pygame.init()

class gal_help():#-----------------------------------------------------------Anleitungen----------------------------------------------------------------------------------------------------------------------------------------------------

    def __init__(self):
        self.main = Tk()
        self.main.geometry("230x80")
        self.main.maxsize(230,80)
        self.main.title("Hilfe")
        self.main.configure(bg = "light green")
        self.hilfelabel = Label(self.main, text = "1. Name vom Bild eingeben", font = 30, bg = "light green")
        self.hilfelabel.grid(row = 0, column = 0, sticky = (W))
        self.hilfelabel1 = Label(self.main, text = "2. Button 'Hinzufügen' drücken", font = 30, bg = "light green")
        self.hilfelabel1.grid(row = 1, column = 0, sticky = (W))
        self.hilfelabel2 = Label(self.main, text = "3. Datei auswählen", font = 30, bg = "light green")
        self.hilfelabel2.grid(row = 2, column = 0, sticky = (W))
        self.main.mainloop()

class mus_help():

    def __init__(self):
        self.main = Tk()
        self.main.geometry("250x80")
        self.main.maxsize(250,80)
        self.main.title("Hilfe")
        self.main.configure(bg = "light green")
        self.hilfelabel = Label(self.main, text = "1. Name vom Lied eingeben", font = 30, bg = "light green")
        self.hilfelabel.grid(row = 0, column = 0, sticky = (W))
        self.hilfelabel1 = Label(self.main, text = "2. Button 'Lied hinzufügen' drücken", font = 30, bg = "light green")
        self.hilfelabel1.grid(row = 1, column = 0, sticky = (W))
        self.hilfelabel2 = Label(self.main, text = "3. Datei auswählen", font = 30, bg = "light green")
        self.hilfelabel2.grid(row = 2, column = 0, sticky = (W))
        self.main.mainloop()

class galerie(): #-------------------------------------------------------------------------------------Galerie--------------------------------------------------------------------------------------------------------------------------------

    def __init__(self):
        self.bliste = []
        self.main = Tk()
        self.main.geometry("300x175")
        self.main.maxsize(300,175)
        self.main.title("Galerie")
        self.main.configure(bg = "#00cccc")
        self.obererFrame = Frame(self.main, bg = "#00cccc")
        self.obererFrame.grid(row = 0, column = 2, padx = 5, pady = 5, sticky = (N))
        self.bilderliste = Listbox(self.main,  bg = "#ffc266")
        self.obererinnerFrame = Frame(self.obererFrame, bg = "#79EABB")
        self.obererinnerFrame.grid(row = 0, column = 0)
        self.bilderliste.grid(row = 0, column = 0,rowspan = 2, sticky =(N,S))
        self.label = Label(self.obererFrame, text = "Dateiname:", bg = "#00cccc")
        self.label.grid(row = 2, column = 0, columnspan = 2, sticky = (S))
        self.eingabe = Entry(self.obererFrame, bg = "#b3ffff")
        self.eingabe.grid(row=3, column = 0, columnspan = 2, sticky = (S))
        self.galerie_help = Button(self.obererFrame, text = "Anleitung",command = lambda: gal_help(), bg = "#79EABB", relief = "flat",  activebackground = "#79EABB")
        self.galerie_help.grid(row = 4, column = 0, sticky = (S), pady = 20)
        self.einfügenb = Button(self.obererinnerFrame, text = "Anzeigen", command = self.öffne_bild, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.einfügenb.grid(row = 0, column = 3, sticky = (W))
        self.bild_hb = Button(self.obererinnerFrame, text = "Hinzufügen", command = self.bild_hinzufügen, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.bild_hb.grid(row=0,column=4, sticky = (W))
        self.bildlb = Button(self.obererinnerFrame, text = "Bild löschen", command = self.bild_löschen, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.bildlb.grid(row = 1, column = 3)
        self.beendeb = Button(self.obererinnerFrame, text = "Beenden", command = self.beenden, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.beendeb.grid(row = 1, column = 4)
        self.main.mainloop()

    def beenden(self):
        self.main.destroy()

    def öffne_bild(self):
        self.new = Toplevel(self.main)
        self.element = self.bilderliste.index("active")
        self.lbbild = Label(self.new, image = self.bliste[self.element])
        self.lbbild.pack()

    def bild_hinzufügen(self):
        self.directory = filedialog.askopenfilename()
        self.bilderliste.insert("end", self.eingabe.get())
        self.file = PhotoImage(file = self.directory)
        self.bliste.append(self.file)

    def bild_löschen(self):
        if self.bilderliste.curselection():
            self.index = bilderliste.curselection()[0]
            self.bliste.pop(self.index)
            self.bilderliste.delete(self.index)

class musicplayer(): #-----------------------------------------------------Music-Player-----------------------------------------------------------------------------------------------------------------------------

    def __init__(self):
        self.main = Tk()
        self.main.geometry("400x175")
        self.main.maxsize(400,175)
        self.main.title("Musikplayer")
        self.main.configure(bg = "#00cccc")
        self.pausiert = False
        self.audio = StringVar()
        self.info = StringVar()
        self.lieder = []
        self.ersterframe = Frame(self.main, bg = "#79EABB")
        self.ersterframe.grid(row = 0, column = 1, sticky = (W,N))
        self.zweiterframe = Frame(self.main, bg = "#00cccc")
        self.zweiterframe.grid(row = 0, column = 2, sticky = (W,N))
        self.dritterframe = Frame(self.ersterframe, bg = "#00cccc")
        self.dritterframe.grid(row = 3, column = 0, columnspan = 2)
        self.liederliste = Listbox(self.main, relief = "flat",  bg = "#ffc266")
        self.liederliste.grid(row = 0, column = 0)
        self.skala = Scale(self.zweiterframe,from_ =0, to=100, orient="horizontal", command= self.lregulierung, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.skala.set(50)
        pygame.mixer.music.set_volume(0.5)
        self.skala.grid(row=2, column=0, sticky = (S), pady = 10)
        self.labeldateiname = Label(self.zweiterframe, text = "Dateiname:", bg = "#00cccc")
        self.labeldateiname.grid(row = 0, column = 0)
        self.eingabe = Entry(self.zweiterframe, relief = "flat", bg = "#b3ffff")
        self.eingabe.grid(row=1,column=0, sticky = (E,S), padx = 10, pady = 10)
        self.lb1 = Label(self.dritterframe, text = "Länge des Liedes: ---", bg = "#00cccc", bd = 8)
        self.lb1.grid(row = 0, column = 0, columnspan = 2, sticky = (W,S))
        self.lb2 = Label(self.dritterframe, text = "Aktuelle Zeit: ---", bg = "#00cccc", bd = 12)
        self.lb2.grid(row = 1, column = 0, columnspan = 2, sticky = (W,N))
        self.playbutton = Button(self.ersterframe, text = "Abspielen", command = self.play, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.playbutton.grid(row = 0,column = 0)
        self.stopbutton = Button(self.ersterframe, text = "Pause", command = self.pause, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.stopbutton.grid(row=0,column=1)
        self.lbutton = Button(self.ersterframe, text = "Lied\nhinzufügen", command = self.lied_hinzufügen, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.lbutton.grid(row = 1, column = 0)
        self.lösche_b = Button(self.ersterframe, text = "Lied\nlöschen", command = self.lied_löschen, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.lösche_b.grid(row = 1, column = 1)
        self.dbutton = Button(self.ersterframe, text = "Stopp", command = self.stop, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.dbutton.grid(row = 2, column = 0)
        self.destroybutton = Button(self.ersterframe, text = "Beenden", command = self.beenden, relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.destroybutton.grid(row = 2, column = 1)
        self.help_button = Button(self.zweiterframe, text = "Anleitung", command = lambda: mus_help(), relief = "flat", bg = "#79EABB", activebackground = "#79EABB")
        self.help_button.grid(row = 3,column = 0, pady = 5, sticky = (S))
        self.liedleiste = Menu(self.main)
        self.ll = Menu(self.liedleiste)
        self.ll.add_command(label = "Lied laden", command = self.liedleiste_lied_hinzufügen)
        self.ll.add_command(label = "Player beenden", command = self.beenden)
        self.ll.add_command(label = "Selbstzerstörung", command = self.lüge)
        self.liedleiste.add_cascade(label = "Dateien", menu = self.ll)
        self.main["menu"] = self.liedleiste
        self.main.mainloop()

    def start_count(self, t):
        self.aktuelle_Zeit = 0
        self.minuten = 0
        while self.aktuelle_Zeit <= t and pygame.mixer.music.get_busy():
            if self.pausiert:
                continue
            else:
                self.a_zeit = ' {}'.format(str(self.aktuelle_Zeit))
                self.minuten_Z = ' {}'.format(str(self.minuten))
                self.lb2["text"] = "Aktuelle Zeit:" + self.minuten_Z + " Minuten und " + self.a_zeit + " Sekunden"
                time.sleep(1)
                self.aktuelle_Zeit += 1
                if self.aktuelle_Zeit > 59:
                    self.minuten += 1
                    self.aktuelle_Zeit = 0

    def play(self):
        if self.pausiert:
            pygame.mixer.music.unpause()
            self.pausiert = False
        else:
            self.element = self.liederliste.index("active")
            pygame.mixer.music.load(self.lieder[self.element])
            pygame.mixer.music.play(0,0.0)
            self.musicPlaying = True
            self.audio = MP3(self.lieder[self.element])
            self.info = str(round(self.audio.info.length))
            self.newinfo = int(self.info)
            self.minuten = round(self.newinfo/60)
            self.sekunden = self.newinfo - self.minuten * 60
            self.lb1["text"] = "Länge des Liedes: {0} Minuten  und {1} Sekunden".format(self.minuten,self.sekunden)
            self.t1 = threading.Thread(target=self.start_count, args=(self.newinfo,))
            self.t1.start()

    def pause(self):
            pygame.mixer.music.pause()
            self.pausiert = True

    def lied_hinzufügen(self):
        self.directory = filedialog.askopenfilename()
        self.liederliste.insert("end", self.eingabe.get())
        self.lieder.append(self.directory)

    def lied_löschen(self):
         if self.liederliste.curselection():
            self.index = self.liederliste.curselection()[0]
            self.lieder.pop(self.index)
            self.liederliste.delete(self.index)

    def stop(self):
        pygame.mixer.music.stop()

    def beenden(self):
        self.main.destroy()
        pygame.mixer.music.stop()

    def lregulierung(self, lautstärke):
        self.lautstärkewert = float(lautstärke) / 100
        pygame.mixer.music.set_volume(self.lautstärkewert)

    def lüge(self):
        self.frequency = 550
        self.duration = 200
        winsound.Beep(self.frequency, self.duration)
        self.antwort = messagebox.askokcancel("Selbstzerstörung", "Selbstzerstörung wurde eingeleitet!")
        if self.antwort == 1:
            self.box_ = Toplevel()
            self.lb = Label(self.box_, text = "Sie wurden hereingelegt!")
            self.lb.pack()
        else:
            self.box_ = Toplevel()
            self.lb = Label(self.box_, text = "Sie wurden hereingelegt!")
            self.lb.pack()

    def einlesen(self):
        self.datei = filedialog.askopenfilename()
        self.liederliste.insert("end", self.eingabe.get())
        self.lieder.append(self.datei)
        self.einfügen.destroy()

    def liedleiste_lied_hinzufügen(self):
         self.einfügen = Toplevel()

         self.button = Button(self.einfügen, text = "Einlesen", command = self.einlesen)
         self.button.grid(row = 0, column = 3)

         self.eingabe = Entry(self.einfügen)
         self.eingabe.grid(row = 0,column = 0, columnspan = 2, padx = 7)

class internetlink():#-----------------------------------------Internetlink öffnen-----------------------------------------------------------------------------------------------------------------------------------------------------------

    def __init__(self):
        self.main = Tk()
        self.main.geometry("270x100")
        self.main.configure(bg = "#0099cc")
        self.main.title("Link-Öffner")
        self.main.maxsize(270,100)
        self.frame = Frame(self.main, bg = "#ff6633", bd = 5)
        self.frame.grid(row = 0, column = 0, sticky = (W,E), padx = 20, pady = 20)
        self.entry = Entry(self.frame, relief = "flat")
        self.entry.grid(row = 0, column = 0)
        self.startbutton = Button(self.frame, text= "Starten", command = self.verbinden, relief = "flat")
        self.startbutton.grid(row = 0, column = 1, padx = 10)
        self.ausgabe = Label(self.main, text = "Bitte etwas eingeben!", font = 20, bg = "#0099cc", anchor = "n")
        self.ausgabe.grid(row = 1, column = 0)

    def verbinden(self):
        self.eingabe = self.entry.get()
        webbrowser.get("windows-default")
        webbrowser.open_new_tab(self.eingabe)

class timer(): #------------------------------------------------------------Timer-------------------------------------------------------------------------------------------------------------------------------------------------------------

    def __init__(self):
        self.sekunden = 0
        self.minuten = 0
        self.gesamtlänge = 0
        self.main = Tk()
        self.main.geometry("400x100")
        self.main.title("Timer")
        self.main.maxsize(400,100)
        self.main.configure(bg = "#0099cc")
        self.obererFrame = Frame(self.main, bg = "#0099cc", bd = 5)
        self.obererFrame.grid(row = 0, column = 0, sticky = (N,S), padx = 10, pady = 10)
        self.obererinnererFrame = Frame(self.obererFrame, bg = "#ff6633", bd = 5)
        self.obererinnererFrame.grid(row = 0, column = 0)
        self.eingabe = Entry(self.obererinnererFrame, relief = "flat")
        self.eingabe.grid(row = 0, column = 0)
        self.eingabe1 = Entry(self.obererinnererFrame, relief = "flat")
        self.eingabe1.grid(row = 0, column = 1, padx = 10)
        self.start_b = Button(self.obererinnererFrame, text = "Start", command = self.starten, relief = "flat")
        self.start_b.grid(row = 0, column = 2, padx = 15)
        self.timelabel = Label(self.obererFrame, text = "---", bg = "#0099cc", fg  = "white")
        self.timelabel.grid(row = 1, column = 0, columnspan = 3, pady = 20)
        self.main.mainloop()

    def timer_count(self,t):
        self.zeitvariable = self.gesamtlänge
        while self.zeitvariable > 0:
                self.a_sec = ' {}'.format(str(self.sekunden))
                self.a_min = ' {}'.format(str(self.minuten))
                self.timelabel.config(text = "{0} : {1} Min.".format(str(self.a_min), str(self.a_sec)))
                time.sleep(1)
                self.sekunden -= 1
                self.zeitvariable -= 1
                if self.sekunden == 0:
                    self.minuten -= 1
                    self.sekunden = 59
                    self.zeitvariable -= 1
        else:
            self.timelabel.config(text = "gesetzter Punkt wurde erreicht!")
            self.frequency = 200
            self.duration = 5000
            winsound.Beep(self.frequency, self.duration)

    def starten(self):
        self.minuten = int(self.eingabe.get())
        self.sekunden = int(self.eingabe1.get())
        self.gesamtlänge = (self.minuten*60) + self.sekunden
        self.timerthread = threading.Thread(target=self.timer_count, args=(self.gesamtlänge,))
        self.timerthread.start()

class hilfe(): #----------------------------------------------------------------------Hilfelabel---------------------------------------------------------------------------------------------------------------------------------------------

    def __init__(self):
        self.main = Tk()
        self.main.geometry("500x200")
        self.main.maxsize(500,200)
        self.main.title("Hilfe")
        self.main.configure(bg = "light green")
        self.hilfelabel = Label(self.main, text = "- Robert Info: zeigt alle Infos zum persönlichen Assistenten", font = 30, bg = "light green")
        self.hilfelabel.grid(row = 0, column = 0, sticky = (W))
        self.hilfelabel1 = Label(self.main, text = "- aktuelle Zeit: Robert zeigt dir das aktuelle Datum und die Uhrzeit", font = 30, bg = "light green")
        self.hilfelabel1.grid(row = 1, column = 0, sticky = (W))
        self.hilfelabel2 = Label(self.main, text = "- Öffne Galerie: Öffnet die Galerie", font = 30, bg = "light green")
        self.hilfelabel2.grid(row = 2, column = 0, sticky = (W))
        self.hilfelabel3 = Label(self.main, text = " - Öffne Player: Öffnet den Musikmediathek", font = 30, bg = "light green")
        self.hilfelabel3.grid(row = 3, column = 0, sticky = (W))
        self.hilfelabel4 = Label(self.main, text = "- Hilfe: Öffnet dieses Label", font = 30, bg = "light green")
        self.hilfelabel4.grid(row = 4, column = 0, sticky = (W))
        self.hilfelabel5 = Label(self.main, text = "- Timer: Öffnet einen Timer", font = 30, bg = "light green")
        self.hilfelabel5.grid(row = 5, column = 0, sticky = (W))
        self.hilfelabel6 = Label(self.main, text = "- Internet: Öffnet ein Programm zum eingeben von Internetlinks", font = 30, bg = "light green")
        self.hilfelabel6.grid(row = 6, column = 0, sticky = (W))
        self.hilfelabel7 = Label(self.main, text = "- Rechner: Öffnet einen kleinen Taschenrechner", font = 30, bg = "light green")
        self.hilfelabel7.grid(row = 7, column = 0, sticky = (W))
        self.main.mainloop()

class Robert: #-------------------------------------------------------------------Klasse Robert(seine Persönlichkeit)-----------------------------------------------------------------------------------------------------------------------

    def __init__(self):
        self.höhe = 80
        self.breite = 200
        self.benutzername = ""
        self.geburtsdatum = "17.6.19 um 22:48"
        self.name = "Robert"
        self.main = Tk()
        self.main.config(width = self.breite, height = self.höhe, bg = "light green")
        self.main.title("Service Assistent")
        self.main.maxsize(500,300)
        self.obererBlock = Frame(self.main, width = 300, height = 200, bg="light green")
        self.obererBlock.grid(row = 0, column = 0, rowspan = 2, columnspan = 1, sticky = (W,E), padx = 5, pady = 5)
        self.eingabe = Entry(self.obererBlock, bg = "light cyan")
        self.eingabe.grid(row = 1, column = 0, sticky = (W))
        self.hauptüberschrift = Label(self.obererBlock, text = "Robert, dein Assistent!", font = "30", bg = "light green")
        self.hauptüberschrift.grid(row = 0,column = 0, sticky = (W))
        self.robertlabel = Label(self.obererBlock, text = "Robert: ", font = "20", bg = "light green", relief = "flat")
        self.robertlabel.grid(row = 2, column = 0, sticky = (W))
        self.searchbutton = Button(self.obererBlock, text = "Suchen", command = lambda: self.search(""), relief = "flat", activebackground = "light cyan", activeforeground = "black", fg = "black", bg = "light cyan")
        self.searchbutton.grid(row = 1, column = 1)
        self.eingabe.bind("<Return>", self.search)
        self.main.mainloop()

    def search(self, enter):
        if "galerie" in self.eingabe.get().lower():
            Galerie = galerie()
        elif "musik" in self.eingabe.get().lower():
            Player = musicplayer()
        elif "internet" in self.eingabe.get().lower():
            Linkseite = internetlink()
        elif "timer" in self.eingabe.get().lower():
            Zähler = timer()
        elif "hilfe" in self.eingabe.get().lower():
            Hilfe = hilfe()
        elif "rechner" in self.eingabe.get().lower():
            os.system("Taschenrechner.pyw")
        elif "willst du mit mir ausgehen?" in self.eingabe.get().lower():
            self.robertlabel["text"] = "Robert: Wenn du bezahlst gerne :)"
        elif "wie schlau bist du?" in self.eingabe.get().lower():
            self.robertlabel["text"] = "Robert: Auf jeden Fall kann ich mehr als eine Kartoffel:)"
        elif "zeit" in self.eingabe.get().lower():
            self.zeit = StringVar()
            self.zeit = time.strftime("Wir haben den %d.%m.%Y  um: %H:%M:%S Uhr")
            self.robertlabel["text"] = "Robert: " + self.zeit
        elif "info" in self.eingabe.get().lower():
            self.robertlabel["text"]  = "Robert:\nName des Assistenten:\n" + self.name + "\nGeburtsdatum:\n " + self.geburtsdatum
        else:
            self.robertlabel["text"] = "Robert: Falsche Eingabe!"

Assistent = Robert()