import json
import random
import time

from common.transaction import auto_meta_sync
from conf.dataapi_settings import BATCH_IP_RESULT_TABLE_ID
from django.utils.translation import ugettext as _

from dataflow.flow import exceptions as Errors
from dataflow.flow.api_models import ResultTable
from dataflow.flow.exceptions import FlowError
from dataflow.flow.handlers.link_rules import NodeInstanceLink
from dataflow.flow.handlers.node_factory import NODE_FACTORY
from dataflow.flow.handlers.node_utils import NodeUtils
from dataflow.flow.models import FlowInfo, FlowNodeInfo, FlowNodeInstance
from dataflow.flow.node_types import NodeTypes
from dataflow.shared.log import flow_logger as logger
from dataflow.shared.meta.project.project_helper import ProjectHelper

def call_func_with_rollback(func, params, rollback_func_info_list=None, retry=3):
    """
    调用某个指定函数，若失败则进行回滚调用
    @param func: 待调用函数引用
    @param params: 待调用函数参数，dict类型
    @param rollback_func_info_list: 回滚函数信息，按顺序回滚 e.g. [{'func': xxx, 'params': {}}], 若为空表示不用回滚
    @param retry: 重试次数
    @return:
    """
    if not rollback_func_info_list:
        rollback_func_info_list = []
    try:
        if params is None:
            return func()
        elif type(params).__name__ == "dict":
            return func(**params)
        else:
            return func(params)
    except Exception as e:
        # 回滚前先打印所有回滚信息
        logger.exception(e)
        for rollback_func_info in rollback_func_info_list:
            rollback_func = rollback_func_info["func"]
            rollback_func_name = getattr(rollback_func, "__name__")
            rollback_params = rollback_func_info["params"]
            logger.info("rollback_func_name: ({}), rollback_params: ({})".format(rollback_func_name, rollback_params))
        # 标记回滚整体是否成功
        rollback_result = True
        for rollback_func_info in rollback_func_info_list:
            rollback_func = rollback_func_info["func"]
            rollback_func_name = getattr(rollback_func, "__name__")
            rollback_params = rollback_func_info["params"]
            current_retry = 0
            # 当前回滚步骤是否成功的标记
            current_func_rollback_result = False
            logger.info(_("开始回滚: %s") % rollback_func_name)
            rollback_exception_message = ""
            while current_retry < retry and not current_func_rollback_result:
                try:
                    if rollback_params is None:
                        rollback_func()
                    else:
                        rollback_func(**rollback_params)
                    current_func_rollback_result = True
                    logger.info(_("回滚成功: %s") % rollback_func_name)
                except FlowError as rollback_exception:
                    time.sleep(1)
                    current_retry = current_retry + 1
                    rollback_exception_message = rollback_exception.message
            if current_retry >= retry:
                # 按顺序回滚，失败则停止回滚，因为回滚操作可能有依赖关系
                rollback_result = False
                logger.exception(
                    _("回滚调用(%(func_name)s)失败-%(message)s")
                    % {
                        "func_name": rollback_func_name,
                        "message": rollback_exception_message,
                    }
                )
                # 回滚列表中一个回滚失败即整体回滚失败
                break
        raise Errors.APIRollbackError(
            _("%(msg1)s%(msg2)s") % {"msg1": "" if rollback_result else "【回滚失败】", "msg2": "{}".format(e)}
        )

class NodeHandler(object):
    """
    节点控制类，所有节点的父节点，继承须知
    1. 必须重载的属性列表 [node_type, node_form, config_attrs]，
    2. 必须重载的方法列表 [build_output]
    """

    # 子类需重载属性
    node_type = None
    node_form = None
    config_attrs = []
    # TODO 这三个变量以后都优化或者删除
    storage_type = None
    default_storage_type = None
    channel_storage_type = None

    def __init__(self, node_id=None, current_node_type=None):
        if node_id is not None:
            node_id = int(node_id)
        self.node_id = node_id
        # 内部变量初始化
        self._node_info = None
        # TODO 元数据操作移到控制内
        self._meta_api_operate_list = {}
        # 标签信息
        self._manage_tags = None

    # ------------------------------- 节点核心操作流程 ------------------------------- #
    def add_node_info(self, username, flow_id, from_links, form_data, frontend_info=None):
        """
        添加节点流程
        """
        from_node_ids = [link["source"]["node_id"] for link in from_links]
        build_form_data = self.build_before(from_node_ids, form_data)

        c_params = self.build_create_params(username, flow_id, build_form_data, frontend_info)

        node = FlowNodeInfo.objects.create(**c_params)
        self.node_id = node.node_id
        self.node_info.create_or_update_link(from_links)
        # 返回表单参数，供控制类使用
        return build_form_data

    def update_node_info(self, username, from_node_ids=None, form_data=None, frontend_info=None):
        """
        更新节点流程
        """
        build_form_data = self.build_before(from_node_ids, form_data, False)
        u_params = self.build_update_params(username, build_form_data, frontend_info)
        for _k, _v in list(u_params.items()):
            setattr(self.node_info, _k, _v)
        self.node_info.save()
        return build_form_data

    def remove_node_info(self):
        """
        删除节点
        """
        self.node_info.delete()
        self._clear_node()

    def to_dict(self, add_has_modify=False, add_result_table_ids=True, add_related_flow=False):
        """
        获取节点信息
        """
        status = self.status
        if add_has_modify:
            has_modify = self.has_modify()
            if has_modify and status in FlowInfo.PROCESS_STATUS:
                status = FlowInfo.STATUS.WARNING
        else:
            has_modify = False

        node_dict = {
            "flow_id": self.flow_id,
            "node_id": self.node_id,
            "node_name": self.name,
            "node_config": self.get_config(),
            "node_type": self.node_type,
            "status": status,
            # 'version': self.version,      # 有性能下降，先忽略
            "version": "",
            "frontend_info": self.node_info.get_frontend_info(),
            "has_modify": has_modify,
        }

        if add_result_table_ids:
            node_dict["result_table_ids"] = self.result_table_ids
        if add_related_flow:
            node_dict["related_flow"] = {
                "status": self.flow.status,
                "flow_name": self.flow.flow_name,
                "project_id": self.flow.project_id,
            }
        return node_dict

    # ------------------------------- 节点通用继承函数 ------------------------------- #
    def build_before(self, from_node_ids, form_data, is_create=True):
        """
        创建、更新节点前的操作，等于 add_before 和 update_before，取决于 is_create 参数
        主要用途：
        1. 参数校验
        2. 部分节点需要从外部获取参数，并更新到 form_data
        """
        # 连线校验
        self.link_rules_check(from_node_ids, form_data, is_create)
        # 静态关联校验
        NodeHandler.kv_source_check(from_node_ids, form_data)
        # 更新节点时判断节点是否有 rt
        if not is_create:
            result_table_ids_num = len(self.result_table_ids)
            if result_table_ids_num == 0:
                raise Errors.NodeError(_("节点(id=%(node_id)s)对应的结果表不存在.") % {"node_id": self.node_id})
        return form_data

    def add_after(self, username, from_node_ids, form_data):
        """
        钩子函数，节点成功添加后回调
        @param {string} username 操作人
        @param {dict} form_data 经过校验清洗后的数据，结构与 self.config_attrs 保持一致
        @param {list} from_node_ids 父节点列表
        """
        return None

    def update_after(self, username, from_node_ids, form_data, prev=None, after=None):
        """
        钩子函数，节点成功更新后回调
        @param {dict} form_data 经过校验清洗后的数据，结构与 self.config_attrs 保持一致
        @param {list} from_node_ids 父节点列表
        @param {dict} prev 变更前的节点数据
        @param {dicr} after 变更后的节点数据
        """
        return None

    def remove_before(self, username):
        """
        钩子函数，节点删除前回调
        """
        pass

    def update_node_metadata_relation(self, bk_biz_id, result_table_ids, heads=None, tails=None):
        """
        更新节点与rt、processing等实体的关联关系
        @param bk_biz_id:
        @param result_table_ids:
        @param heads: 只有计算节点有 heads，tails 参数
        @param tails:
        """
        raise NotImplementedError("节点类型（%s）未定义update_node_relation方法." % self.node_type)

    # ------------------------------- 节点通用普通函数 ------------------------------- #
    def get_config(self, loading_latest=True):
        """
        获取节点当前配置
        """
        return self.clean_config(self.node_info.load_config(loading_latest), False)

    def build_create_params(self, username, flow_id, form_data, frontend_info=None):
        """
        组装节点的新建参数
        """
        node_type = self.node_type

        c_params = {
            "flow_id": flow_id,
            "node_name": form_data["name"],
            "node_type": node_type,
            "status": "no-start",
            "node_config": json.dumps(form_data),
            "latest_version": NodeUtils.gene_version(),
            "frontend_info": json.dumps(frontend_info),
            "created_by": username,
            "updated_by": username,
            "description": "",
        }
        return c_params

    def build_update_params(self, username, form_data, frontend_info=None):
        """
        组装节点的更新参数
        """
        u_params = {
            "node_id": self.node_id,
            "node_config": json.dumps(form_data),
            "frontend_info": json.dumps(frontend_info),
            "updated_by": username,
        }
        return u_params

    @classmethod
    def init_by_node(cls, node, current_node_type=None):
        node_handler = cls(node.node_id, current_node_type)
        node_handler._node_info = node
        return node_handler

    def get_node_type_display(self):
        """
        获取节点类型名称
        """
        if self.node_type in NodeTypes.DISPLAY:
            return NodeTypes.DISPLAY[self.node_type]
        else:
            return self.node_type

    def clean_config(self, config, is_create):
        """
        按照 cls.config_attrs 返回约定结构，可以设置部分字段的默认值，未来可以在结构中定义
        类型、默认值等属性，结合这个方法来规整 config 的返回
        """
        form = self.node_form(config, is_create=is_create)
        if not form.is_valid():
            _err_msg = "[Node Error] invalid node config, node_id={}, error={},".format(self.node_id, form.errors)
            if hasattr(form, "format_errmsg"):
                _err_msg = form.format_errmsg()
            logger.error(_err_msg)
            raise Errors.NodeValidError(_err_msg)

        form_data = form.cleaned_data

        # 避免 Form 表单解析，丢了部分关键字段，此处做了 None 值补全
        return {
            _attr: form_data[_attr] if _attr in form_data else None
            # for _attr in get_all_fields_from_form(form)
            for _attr in self.config_attrs
        }

    @classmethod
    def kv_source_check(cls, from_node_ids, form_data):
        from_node_ids_num = len(from_node_ids)
        if from_node_ids_num > 0:
            from_nodes = NodeUtils.list_from_nodes_handler(from_node_ids)
            bk_biz_ids = []
            for _n in from_nodes:
                # 除了以下两种情况，暂不支持不同业务的数据关联操作
                # 1. 上游为 kv_source 且是公有的
                # 2. 上游为 IP 库的离线数据源(TODO: 支持离线关联数据源后可能需要取消)
                if not (
                    (_n.node_type == NodeTypes.BATCH_SOURCE and _n.result_table_id in BATCH_IP_RESULT_TABLE_ID)
                    or (_n.node_type in NodeTypes.ALL_KV_SOURCE_CATEGORY and ResultTable(_n.result_table_id).is_public)
                ):
                    bk_biz_ids.append(_n.get_config(False)["bk_biz_id"])
            # 暂不支持仅公有静态关联结果表的数据关联操作
            # 即上游都是公有(1个或多个)静态关联是不允许的，不区分跨不跨业务
            # 这种情形恰好包含了 591_iplib 和 591_ipv6lib 单独作为上游离线静态关联数据源的情况
            # TODO: 到时若有结果表从私有上升为公有需要调整逻辑
            if len(set(bk_biz_ids)) < 1:
                raise Errors.NodeValidError(_("暂不支持仅公开数据的计算操作"))
            # 暂不支持所有情况下多个私有业务的数据关联操作
            if len(set(bk_biz_ids)) > 1:
                raise Errors.NodeValidError(_("暂不支持不同业务的数据关联操作"))
            # 将原始数据节点的业务 ID 传递
            # 下游业务必须继承私有的关联节点业务或普通节点业务
            # TODO: 目前公开数据只可能是 591 业务的数据，若之后出现非 591 业务的公有数据，可能需要调整逻辑
            if form_data["bk_biz_id"] not in bk_biz_ids:
                raise Errors.NodeValidError(_("出现非法业务ID(%(bk_biz_id)s)") % {"bk_biz_id": form_data["bk_biz_id"]})

    def link_rules_check(self, from_node_ids, form_data, is_create):
        node_type = self.node_type
        from_nodes = NodeUtils.list_from_nodes_handler(from_node_ids)
        from_result_table_ids = NodeUtils.get_from_result_table_ids(node_type, form_data)
        graph_from_result_table_ids = []
        if len(from_nodes) > len(from_result_table_ids):
            # 若用户为某个支持多个父节点的节点增加具有相同输入 RT 的上游节点，可能绕过下一步的校验
            # 这里确保节点数量大于上游 RT 的数量
            raise Errors.NodeValidError(_("连线配置错误，当前修改了上游节点，需要重新保存"))
        for _n in from_nodes:
            # 【2.2校验】
            _n_from_result_table_ids = _n.result_table_ids
            # 上游任意一个节点的所有rt必须至少有一个作为当前节点的接入rt
            judge_num = len(set(_n_from_result_table_ids) & set(from_result_table_ids))
            if judge_num == 0:
                raise Errors.NodeValidError(
                    _("节点(%(node_name)s)连线错误，其上游节点(%(from_node_name)s)的结果表应被当前节点指定使用%(message)s")
                    % {
                        "from_node_name": _n.name,
                        "node_name": form_data["name"],
                        "message": "" if is_create else _("，请检查节点连线配置并重新保存"),
                    }
                )
            # 收集当前节点在画布离线的所有上游节点的rt
            graph_from_result_table_ids.extend(_n_from_result_table_ids)
        # 2. 此时from_result_table_ids为旧信息，因此需要考虑两种情况
        # 2.1 当前画布连线rt列表需包含from_result_table_ids，设想一下用户删除了上游节点，然后直接启动
        if node_type not in NodeTypes.SOURCE_CATEGORY and len(
            set(graph_from_result_table_ids) & set(from_result_table_ids)
        ) < len(set(from_result_table_ids)):
            raise Errors.NodeValidError(
                _("连线错误，节点不可使用未经连线的父结果表，请重新保存节点(%(node_name)s)") % {"node_name": form_data["name"]}
            )
        # 3. 校验连线规则
        for _n in from_nodes:
            # 1. 校验上游节点可否连接到当前节点
            # 2. 校验上游节点数量、以及上游节点的下游节点数量是否符合要求
            # 取交集，只取出这个节点有用的 rt 输出, 考虑到可能会有多输出 rt，但只有其中某些被用到的情况
            _n_from_result_table_ids = list(set(_n.result_table_ids) & set(from_result_table_ids))
            NodeInstanceLink.validate_node_link(_n, self, _n_from_result_table_ids, from_nodes, is_create)

    def get_frontend_info(self):
        return self.node_info.get_frontend_info()

    def get_from_nodes_handler(self):
        return [NODE_FACTORY.get_node_handler(_id) for _id in self.from_node_ids]

    def get_to_nodes_handler(self):
        return [NODE_FACTORY.get_node_handler(_id) for _id in self.to_node_ids]

    @auto_meta_sync(using="default")
    def set_status(self, status):
        """
        设置节点状态
        @todo 设置 running 状态是，需要把当前的版本标记为 running_version
        """
        if status in [FlowInfo.STATUS.RUNNING]:
            self.node_info.running_version = self.node_info.latest_version

        self.node_info.status = status
        self.node_info.save()

    def is_new_version(self):
        """
        节点是否是增加的未启动的节点
        @return:
        """
        return not self.running_version

    def is_modify_version(self, ignore_running=False):
        """
        节点是否修改了配置，比较的标准
            1. 当前处于运行过程而与当前记录的最新版本号不同。或者由于节点使用的udf发布了新的版本，则当前节点为修改状态
            2. 节点运行过程中的上游连线发生增减(该情况不存在，因为运行节点及运行节点间连线不允许修改)
        @param ignore_running: 是否忽略节点的的运行状态比对是否是新版本
        @return:
        """
        # 检查标准1
        if (ignore_running or self.status in [FlowInfo.STATUS.RUNNING]) and (
            self.running_version != self.latest_version
            or (
                self.node_type
                in [
                    NodeTypes.STREAM,
                    NodeTypes.BATCH,
                    NodeTypes.BATCHV2,
                ]
                and NodeUtils.is_modify_dp_node_with_udf(self.processing_id)
            )
        ):
            return True
        # 检查标准2
        # 一种比较复杂的判断方式：
        #   通过获取当前节点运行版本对应的所有 version_ids，关联 reversion 获取当时的连线与现在的进行对比
        # 采用一种简单的方式：
        #   获取节点的 from_result_table_ids，与当前上游进行对比(对比方式为 from_result_table_ids 必须各属于上游节点)
        #   若 from_result_table_ids 多了表明删除了上游连线，少了表明增加了上游连线
        if ignore_running or self.status in [FlowInfo.STATUS.RUNNING]:
            from_nodes = self.get_from_nodes_handler()
            # 一条连线只能有一个输入
            len_from_nodes = len(from_nodes)
            if (
                self.node_type in NodeTypes.SOURCE_CATEGORY
                and self.node_type not in NodeTypes.NEW_FROM_RESULT_TABLE_IDS_CATEGORY
            ):
                len_from_nodes = len_from_nodes + 1
            if len(self.from_result_table_ids) != len_from_nodes:
                return True
            current_from_node_rts = []
            for from_node in from_nodes:
                current_from_node_rts.append(from_node.result_table_ids)
            for current_from_node_rt in current_from_node_rts:
                # 小于等于0是考虑上游节点可能是多输出，当前节点又是多输入
                if len(set(current_from_node_rt) & set(self.from_result_table_ids)) != 1:
                    return True
        return False

    def has_modify(self):
        """
        节点是否在运行状态下，修改了配置未重启
        """
        if self.status in [FlowInfo.STATUS.RUNNING] and self.running_revision_version is not None:
            # 比较运行版本和当前节点版本对应的 reversion_version 记录的 object_id 是否一致
            # 查看包含udf的node节点是否改为修改状态

            return self.running_revision_version != self.revision_version or (
                self.node_type
                in [
                    NodeTypes.STREAM,
                    NodeTypes.BATCH,
                    NodeTypes.BATCHV2,
                ]
                and NodeUtils.is_modify_dp_node_with_udf(self.processing_id)
            )
        return False

    def _clear_node(self):
        self.node_id = None
        self._node_info = None

    def _reset_node(self):
        self._node_info = None

    @staticmethod
    def _gene_default_link_id(node_id=None):
        """
        生成连线的端点id
        据悉，对于APP画布而言，初始化节点的时候node_id还没生成，需要一个id，之后有node_id后就不用这个id
        对于FlowAPI，可以不用生成这个id，这里为保证结构一致，也返回一个id，应尽量保持不冲突
        @param node_id:
        @return:
        """
        if node_id:
            return "ch_%s" % node_id
        else:
            return "ch_r%s" % str(random.randint(0, 9999)).zfill(4)

    def get_downstream_nodes(self, filter_node_types=None):
        """
        获取节点的所有下游节点
        @param filter_node_types: 指定特定节点类型
        @return:
        """
        arr = [self]
        nodes = {self.node_id: self}
        # 这里采用循环，主要是为了同时避免环的存在导致死循环，不建议使用递归
        arr_num = len(arr)
        while arr_num > 0:
            _n = arr.pop()
            for _to_node in _n.get_to_nodes_handler():
                if _to_node.node_id in nodes:
                    continue
                if filter_node_types and _to_node.node_type not in filter_node_types:
                    continue
                nodes[_to_node.node_id] = _to_node
                arr.append(_to_node)
        return list(nodes.values())

    def validate_upstream_config(self):
        from_result_table_ids = self.from_result_table_ids
        from_nodes = self.get_from_nodes_handler()
        if len(from_result_table_ids) < len(from_nodes):
            return False
        _from_nodes_rts = []
        for from_node in from_nodes:
            _from_node_rts = from_node.result_table_ids
            _from_nodes_rts.extend(_from_node_rts)
            # 若上游节点的 RT 不在当前节点的 from_result_table_ids 中, 增加了连线未保存
            if len(set(_from_node_rts) & set(from_result_table_ids)) == 0:
                return False
        # 若当前节点存在上游节点没有的 result_table_id, 删除了连线未保存
        if set(from_result_table_ids) - set(_from_nodes_rts):
            return False
        return True

    def get_job(self):
        """
        获取节点对应的job object
        :return:
        """
        # 一个节点对应的job，对应多个必定有问题
        _job_set = self.node_info.job_set
        if _job_set:
            if len(_job_set) > 1:
                raise Errors.NodeError(_("节点不可对应多个任务."))
            return _job_set[0]
        else:
            return None

    # ------------------------------- 通用类属性函数 ------------------------------- #

    @property
    def node_info(self):
        if self._node_info is None:
            self._node_info = FlowNodeInfo.objects.get(node_id=self.node_id)

        return self._node_info

    @property
    def flow_id(self):
        return self.node_info.flow_id

    @property
    def manage_tags(self):
        if self._manage_tags is None:
            manage_tags = ProjectHelper.get_project(self.project_id).get("tags").get("manage", {})
            self._manage_tags = manage_tags
        return self._manage_tags

    @property
    def geog_area_codes(self):
        tag_codes = [_info["code"] for _info in self.manage_tags.get("geog_area", [])]
        if not tag_codes:
            raise Errors.NodeError(_("当前任务所属项目地域信息不存在"))
        if len(tag_codes) > 1:
            raise Errors.NodeError(_("当前任务所属项目地域信息不唯一"))
        return tag_codes

    @property
    def project_id(self):
        return self.flow.project_id

    @property
    def flow(self):
        return self.node_info.flow

    @property
    def from_node_ids(self):
        return self.node_info.from_node_ids

    @property
    def to_node_ids(self):
        return self.node_info.to_node_ids

    @property
    def result_table_ids(self):
        return self.node_info.get_tail_rts()

    @property
    def from_nodes_info(self):
        """
        返回当前节点所有上游节点连线信息
        @return:
            [
                {
                    'id': 3,
                    'from_result_table_ids': ['1_xxxx']
                }
            ]
        """
        _from_nodes_info = []
        from_result_table_ids = set(self.from_result_table_ids)
        for from_node in self.get_from_nodes_handler():
            _from_nodes_info.append(
                {
                    "id": from_node.node_id,
                    "from_result_table_ids": list(set(from_node.result_table_ids) & from_result_table_ids),
                }
            )
        return _from_nodes_info

    @property
    def from_result_table_ids(self):
        """
        获取当前节点的源rt列表
        @return:
        """
        return NodeUtils.get_from_result_table_ids(self.node_type, self.get_config(False))

    @property
    def head_result_table_id(self):
        """
        获取当前节点的头部RT
        @return:
        """
        return self.node_info.get_head_rts()[0]

    @property
    def name(self):
        """
        默认取节点中文名称
        """
        _config = self.get_config(False)
        if "name" in _config:
            return _config["name"]

        return self.node_info.node_name

    @property
    def latest_version(self):
        """
        Flow 生成的特定版本号
        @return:
        """
        return self.node_info.latest_version

    @property
    def running_version(self):
        """
        Flow 生成的特定版本号
        @return:
        """
        return self.node_info.running_version

    @property
    def revision_version(self):
        """
        返回 revision 记录的版本号，即主键ID
        @return:
        """
        return self.node_info.revision_version

    @property
    def running_revision_version(self):
        """
        返回 revision 记录的版本号，即主键ID
        @return:
        """
        return self.node_info.running_revision_version

    @property
    def status(self):
        return self.node_info.status

    @property
    def display_status(self):
        if self.status == FlowInfo.STATUS.FAILURE or not self.validate_upstream_config():
            return FlowInfo.STATUS.FAILURE
        return self.status

    @property
    def is_support_debug(self):
        """
        是否支持调试
        """
        return FlowNodeInstance.get_node_instance(self.node_type)["support_debug"]

    @property
    def frontend_info(self):
        return self.node_info.get_frontend_info()

    @frontend_info.setter
    @auto_meta_sync(using="default")
    def frontend_info(self, info):
        """
        更新节点前端配置

        @param {Dict} info 新的前端配置
        """
        self.node_info.frontend_info = json.dumps(info)
        self.node_info.save(update_fields=["frontend_info"])

    @property
    def is_batch_node(self):
        """
        特指可拥有默认 HDFS 存储的节点，不包括离线数据源
        @return:
        """
        return self.node_type in [
            NodeTypes.BATCH,
            NodeTypes.BATCHV2,
            NodeTypes.MODEL_APP,
            NodeTypes.DATA_MODEL_BATCH_INDICATOR,
        ] or (
            self.node_type
            in [
                NodeTypes.MODEL,
                NodeTypes.PROCESS_MODEL,
                NodeTypes.MODEL_TS_CUSTOM,
            ]
            and self.serving_mode == "offline"
        )

    @property
    def channel_mode(self):
        """
        获取当前节点要运行起来所需要的 channel_mode
        @return:
        """
        channel_mode = "stream"
        # 当前 channel mode 用于 databus 区分是实时还是离线类型，用于判断是否创建对应的 DT(HDFS -> kafka)
        # 可认为离线 TDW 节点是需要创建这个 DT 的，因此这里若上游为离线 TDW 节点，传的是 stream
        if self.node_type == NodeTypes.BATCH_SOURCE or self.is_batch_node:
            channel_mode = "batch"
        elif self.node_type in NodeTypes.SOURCE_CATEGORY:
            # 对于数据源而言，channel_mode 设置为其它
            channel_mode = "other"
        return channel_mode

    @property
    def is_belong_to_topology_job(self):
        """
        标识当前节点若启动时，是否属于拓扑作业
        @return:
        """
        return False

    @property
    def count_freq(self):
        """
        当前节点统计频率，默认为 None
        @return:
        """
        return None

    @property
    def freq_seconds(self):
        """
        当前节点按秒的统计频率，默认为 None
        @return:
        """
        return None

    @property
    def delay_time(self):
        return 0


import json
import pika

class QueuePusher(object):

    def __init__(self, queue_server: str, queue_port: int, queue_name: str):
        super().__init__()
        self._queue_name = queue_name
        self._connection = pika.BlockingConnection(
            pika.ConnectionParameters(queue_server, queue_port))
        self._channel = self._connection.channel()

    def __del__(self):
        _connection = getattr(self, "_connection", None)
        if _connection and _connection.is_open:
            _connection.close()

    def put_message_to_queue(self, message: dict) -> bool:
        if self._channel:
            return self._channel.basic_publish(
                exchange='',
                routing_key=self._queue_name,
                body=json.dumps(message),
                properties=pika.BasicProperties(delivery_mode=2))
        raise Exception("Channel is not set")


#!/usr/bin/env python
# coding=utf-8
'''
@Author: wjm
@Date: 2019-10-13 23:12:52
LastEditTime: 2020-11-25 23:00:57
@Description: file content
'''
import os, math, torch,cv2
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
from utils.vgg import VGG
import torch.nn.functional as F
# from model.deepfuse import MEF_SSIM_Loss
class newLoss(nn.Module):
    def __init__(self,r1=1.1,r2=1000,offset=0.9995):
        super(newLoss, self).__init__()
        self.r1=r1
        self.r2=r2
        self.offset=offset
        print("new loss maked")
    def forward(self, input,target):
        del_x=torch.abs(input-target)
        compare_mut=torch.ones_like(del_x)
        y = ((del_x*255)**self.r1/255)*del_x*1#((torch.minimum(self.offset+del_x,compare_mut))**self.r2)
        return torch.sum(y)

def maek_optimizer(opt_type, cfg, params):
    if opt_type == "ADAM":
        optimizer = torch.optim.Adam(params, lr=cfg['schedule']['lr'], betas=(cfg['schedule']['beta1'], cfg['schedule']['beta2']), eps=cfg['schedule']['epsilon'])
    elif opt_type == "SGD":
        optimizer = torch.optim.SGD(params, lr=cfg['schedule']['lr'], momentum=cfg['schedule']['momentum'])
    elif opt_type == "RMSprop":
        optimizer = torch.optim.RMSprop(params, lr=cfg['schedule']['lr'], alpha=cfg['schedule']['alpha'])
    else:
        raise ValueError
    return optimizer

def make_loss(loss_type):
    # loss = {}
    if loss_type == "MSE":
        loss = nn.MSELoss(reduction='sum')
    elif loss_type == "L1":
        loss = nn.L1Loss(reduction='sum')
    elif loss_type == "MEF_SSIM":
        loss = MEF_SSIM_Loss()
    elif loss_type == "VGG22":
        loss = VGG(loss_type[3:], rgb_range=255)
    elif loss_type == "VGG54":
        loss = VGG(loss_type[3:], rgb_range=255)
    elif loss_type == "newloss":
        loss = newLoss()

    else:
        raise ValueError
    return loss

def get_path(subdir):
    return os.path.join(subdir)

def save_config(time, log):
    open_type = 'a' if os.path.exists(get_path('./log/' + str(time) + '/records.txt'))else 'w'
    log_file = open(get_path('/home/manman/xuanhua/newpan/log/' + str(time) + '/records.txt'), open_type)
    log_file.write(str(log) + '\n')

def save_net_config(time, log):
    open_type = 'a' if os.path.exists(get_path('./log/' + str(time) + '/net.txt'))else 'w'
    log_file = open(get_path('/home/manman/xuanhua/newpan/log/' + str(time) + '/net.txt'), open_type)
    log_file.write(str(log) + '\n')

def calculate_psnr(img1, img2, pixel_range=255, color_mode='rgb'):
    # transfer color channel to y
    if color_mode == 'rgb':
        img1 = (img1 * np.array([0.256789, 0.504129, 0.097906])).sum(axis=2) + 16 / 255 * pixel_range
        img2 = (img2 * np.array([0.256789, 0.504129, 0.097906])).sum(axis=2) + 16 / 255 * pixel_range
    elif color_mode == 'yuv':
        img1 = img1[:, 0, :, :]
        img2 = img2[:, 0, :, :]
    elif color_mode == 'y':
        img1 = img1
        img2 = img2
    # img1 and img2 have range [0, pixel_range]
    img1 = img1.astype(np.float64)
    img2 = img2.astype(np.float64)
    mse = np.mean((img1 - img2)**2)
    if mse == 0:
        return float('inf')
    return 20 * math.log10(pixel_range / math.sqrt(mse))

def ssim(img1, img2, pixel_range=255, color_mode='rgb'):
    C1 = (0.01 * pixel_range)**2
    C2 = (0.03 * pixel_range)**2

    # transfer color channel to y
    if color_mode == 'rgb':
        img1 = (img1 * np.array([0.256789, 0.504129, 0.097906])).sum(axis=2) + 16 / 255 * pixel_range
        img2 = (img2 * np.array([0.256789, 0.504129, 0.097906])).sum(axis=2) + 16 / 255 * pixel_range
    elif color_mode == 'yuv':
        img1 = img1[:, 0, :, :]
        img2 = img2[:, 0, :, :]
    elif color_mode == 'y':
        img1 = img1
        img2 = img2

    img1 = img1.astype(np.float64)
    img2 = img2.astype(np.float64)
    kernel = cv2.getGaussianKernel(11, 1.5)
    window = np.outer(kernel, kernel.transpose())

    mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5]  # valid
    mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5]
    mu1_sq = mu1**2
    mu2_sq = mu2**2
    mu1_mu2 = mu1 * mu2
    sigma1_sq = cv2.filter2D(img1**2, -1, window)[5:-5, 5:-5] - mu1_sq
    sigma2_sq = cv2.filter2D(img2**2, -1, window)[5:-5, 5:-5] - mu2_sq
    sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2

    ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / (
        (mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2))
    return ssim_map.mean()

def calculate_ssim(img1, img2, pixel_range=255):
    '''calculate SSIM
    the same outputs as MATLAB's
    img1, img2: [0, 255]
    '''
    if not img1.shape == img2.shape:
        raise ValueError('Input images must have the same dimensions.')
    if img1.ndim == 2:
        return ssim(img1, img2, pixel_range)
    elif img1.ndim == 3:
        if img1.shape[2] == 3:
            ssims = []
            for i in range(3):
                ssims.append(ssim(img1, img2, pixel_range))
            return np.array(ssims).mean()
        elif img1.shape[2] == 1:
            return ssim(np.squeeze(img1), np.squeeze(img2), pixel_range)
    else:
        raise ValueError('Wrong input image dimensions.')


# not needed
# from PIL import Image
import pytesseract
import cv2
# we might need for speed
import imutils
from imutils.video import VideoStream

def ocr_core(filename):
    text = pytesseract.image_to_string(Image.open(filename))
    return text

#print(ocr_core('example.png'))

# see image_to_data
# https://stackoverflow.com/questions/20831612/getting-the-bounding-box-of-the-recognized-words-using-python-tesseract
cap = cv2.VideoCapture(0)

while True:
	# read image ----
	ret, img = cap.read()
	if ret:
		# process image ---
		# if needed grayscale
		gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
		# blur
		gray = cv2.GaussianBlur(gray, (3,3), 0)
		img2 = cv2.GaussianBlur(img, (31,31), 0)
		# if needed do some sort of edge detection to reduce ROI
		mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
		# get ROI to work and crop image
		# invert the mask
		#mask = 255 - mask

		condition = False
		# only call tesseract if needed
		if condition:
			print(pytesseract.image_to_string(img))
			# feed to tesseract
			d = pytesseract.image_to_data(img, output_type=pytesseract.Output.DICT)
			# there might be good stuff inside here --> print(d.keys())
			n_boxes = len(d['level'])

			for i in range(n_boxes):
			    (x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])
			    cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)

		cv2.imshow('img', img)
		cv2.imshow("mask", mask)
		cv2.imshow("img-blur", img2)
		k = cv2.waitKey(1)
		if k == ord("q"):
			break

cv2.destroyAllWindows()


"""
Functions that define and manipulate images. Images are just data and a World Coordinate System.
"""

__all__ = ["image_channel_iter", "image_null_iter", "image_raster_iter"]

import logging
import collections.abc

import numpy

from rascil.data_models.memory_data_models import Image

from rascil.processing_components.image.operations import (
    create_image_from_array,
    create_empty_image_like,
    image_is_canonical,
)
from rascil.processing_components.util.array_functions import tukey_filter

log = logging.getLogger("rascil-logger")

def image_null_iter(im: Image, facets=1, overlap=0) -> collections.abc.Iterable:
    """One time iterator

    This is useful to simplify control structures.

    :param im:
    :param facets: Number of image partitions on each axis (2)
    :param overlap: overlap in pixels
    :return:
    """
    yield im

def image_raster_iter(
    im: Image, facets=1, overlap=0, taper="flat", make_flat=False
) -> collections.abc.Iterable:
    """Create an image_raster_iter generator, returning a list of subimages, optionally with overlaps

    The WCS is adjusted appropriately for each raster element. Hence this is a coordinate-aware
    way to iterate through an image.

    The argument make_flat means that the subimages contain constant values. This is useful for
    dealing with overlaps in gather operations.

    Provided we don't break reference semantics, memory should be conserved. However make_flat
    creates a new set of images and thus reference semantics dont hold.

    To update the image in place::

        for r in image_raster_iter(im, facets=2):
            r["pixels"].data[...] = numpy.sqrt(r["pixels"].data[...])

    Note that some combinations of image size, facets, and overlap are invalid. In these cases,
    an exception (ValueError) is raised.

    In the case where make_flat is true, the subimages returned have tapers applied in the overlap
    region. This is used by py:func:`rascil.processing_components.image.gather_scatter.image_gather_facets`
    to merge subimages into one image.

    A taper is applied in the overlap regions. None implies a constant value, linear is a ramp,
    quadratic is parabolic at the ends, and tukey is the tukey function.

    :param im: Image
    :param facets: Number of image partitions on each axis (2)
    :param overlap: overlap in pixels
    :param taper: method of tapering at the edges: 'flat' or 'linear' or 'quadratic' or 'tukey'
    :param make_flat: Make the flat images
    :returns: Generator of images

    See also
        :py:func:`rascil.processing_components.image.gather_scatter.image_gather_facets`
        :py:func:`rascil.processing_components.image.gather_scatter.image_scatter_facets`
        :py:func:`rascil.processing_components.util.array_functions.tukey_filter`
    """

    assert image_is_canonical(im)

    if im is None:
        return im

    nchan, npol, ny, nx = im["pixels"].data.shape
    assert facets <= ny, "Cannot have more raster elements than pixels"
    assert facets <= nx, "Cannot have more raster elements than pixels"

    assert facets >= 1, "Facets cannot be zero or less"
    assert overlap >= 0, "Overlap must be zero or greater"

    if facets == 1:
        yield im
    else:

        if overlap >= (nx // facets) or overlap >= (ny // facets):
            raise ValueError(
                f"Overlap in facets is too large {nx}, {facets}, {overlap}"
            )

        # Size of facet
        dx = nx // facets
        dy = ny // facets

        # Step between facets
        sx = dx - 2 * overlap
        sy = dy - 2 * overlap

        def taper_linear(npixels, over):
            taper1d = numpy.ones(npixels)
            ramp = numpy.arange(0, over).astype(float) / float(over)

            taper1d[:over] = ramp
            taper1d[(npixels - over) : npixels] = 1.0 - ramp
            return taper1d

        def taper_quadratic(npixels, over):
            taper1d = numpy.ones(npixels)
            ramp = numpy.arange(0, over).astype(float) / float(over)

            quadratic_ramp = numpy.ones(over)
            quadratic_ramp[0 : over // 2] = 2.0 * ramp[0 : over // 2] ** 2
            quadratic_ramp[over // 2 :] = 1 - 2.0 * ramp[over // 2 : 0 : -1] ** 2

            taper1d[:over] = quadratic_ramp
            taper1d[(npixels - over) : npixels] = 1.0 - quadratic_ramp
            return taper1d

        def taper_tukey(npixels, over):

            xs = numpy.arange(npixels) / float(npixels)
            r = 2 * over / npixels
            taper1d = [tukey_filter(x, r) for x in xs]

            return taper1d

        def taper_flat(npixels, over):
            return numpy.ones([npixels])

        i = 0
        for fy in range(facets):
            y = ny // 2 + sy * (fy - facets // 2) - overlap
            for fx in range(facets):
                x = nx // 2 + sx * (fx - facets // 2) - overlap
                if x < 0 or x + dx > nx:
                    raise ValueError(f"overlap too large: starting point {x}")
                wcs = im.image_acc.wcs.deepcopy()
                wcs.wcs.crpix[0] -= x
                wcs.wcs.crpix[1] -= y
                # yield image from slice (reference!)
                subim = create_image_from_array(
                    im["pixels"].data[..., y : y + dy, x : x + dx],
                    wcs,
                    im.image_acc.polarisation_frame,
                )
                if overlap > 0 and make_flat:
                    flat = create_empty_image_like(subim)
                    if taper == "linear":
                        flat["pixels"].data[..., :, :] = numpy.outer(
                            taper_linear(dy, overlap), taper_linear(dx, overlap)
                        )
                    elif taper == "quadratic":
                        flat["pixels"].data[..., :, :] = numpy.outer(
                            taper_quadratic(dy, overlap), taper_quadratic(dx, overlap)
                        )
                    elif taper == "tukey":
                        flat["pixels"].data[..., :, :] = numpy.outer(
                            taper_tukey(dy, overlap), taper_tukey(dx, overlap)
                        )
                    else:
                        flat["pixels"].data[..., :, :] = numpy.outer(
                            taper_flat(dy, overlap), taper_flat(dx, overlap)
                        )
                    yield flat
                else:
                    yield subim
                i += 1

def image_channel_iter(im: Image, subimages=1) -> collections.abc.Iterable:
    """Create a image_channel_iter generator, returning images

    The WCS is adjusted appropriately for each raster element. Hence this is a coordinate-aware
    way to iterate through an image.

    Provided we don't break reference semantics, memory should be conserved

    To update the image in place::

        for r in image_channel_iter(im, subimages=nchan):
            r.data[...] = numpy.sqrt(r.data[...])

    :param im: Image
    :param subimages: Number of subimages
    :returns: Generator of images

    See also
        :py:func:`rascil.processing_components.image.gather_scatter.image_gather_channels`
        :py:func:`rascil.processing_components.image.gather_scatter.image_scatter_channels`
    """

    assert image_is_canonical(im)

    nchan, npol, ny, nx = im["pixels"].data.shape

    assert subimages <= nchan, "More subimages %d than channels %d" % (subimages, nchan)
    step = nchan // subimages
    channels = numpy.array(range(0, nchan, step), dtype="int")
    assert (
        len(channels) == subimages
    ), "subimages %d does not match length of channels %d" % (subimages, len(channels))

    for i, channel in enumerate(channels):
        if i + 1 < len(channels):
            channel_max = channels[i + 1]
        else:
            channel_max = nchan

        # Adjust WCS
        wcs = im.image_acc.wcs.deepcopy()
        wcs.wcs.crpix[3] -= channel

        # Yield image from slice (reference!)
        yield create_image_from_array(
            im["pixels"].data[channel:channel_max, ...],
            wcs,
            im.image_acc.polarisation_frame,
        )


import os
import random
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import streamlit as st
import pickle
import requests
import sklearn

# Set the page configuration
st.set_page_config(
    page_title="Titanic Survival Predictor 🚢",
    page_icon="🚢",
    layout="centered",
    initial_sidebar_state="expanded",
    menu_items={
        'Get Help': None,
        'Report a bug': None,
        'About':
        """ ## Welcome to the Titanic Survival Predictor! 🌟
        This app uses AI to estimate your chances of survival on the Titanic. Feel free to contact me if you have any questions or feedback:
        - 📧 [My email](mailto:atalhabektas@gmail.com)
        - 🔗 [My LinkedIn](https://www.linkedin.com/in/ahmet-talha-bekta%C5%9F-056844216)
        - 💻 [My GitHub](https://github.com/ahmettalhabektas)
        - 👨💻 [My Kaggle](https://www.kaggle.com/ahmettalhabektas)
        Made with 💖 by Ahmet Talha Bektaş
        """
    }
)

# Read the Titanic dataset
#titanic = pd.read_csv(CSV_PATH)
PATH = "https://raw.githubusercontent.com/ahmettalhabektas/Streamlit/master/Titanic_Streamlit/data/titanic.csv"
titanic = pd.read_csv(PATH)

# Language selection
lang = st.selectbox("🌐 Please select a language", options=( "🇬🇧 English","🇹🇷 Türkçe"))
lang = lang.split(" ")[1]

if lang == "English":
    # English Content
    st.markdown("# Embark on a Titanic Adventure 🚢")
    st.write(
        "Welcome aboard the RMS Titanic, a legendary British passenger liner with a captivating story! Join us as we journey back in time to April 1912, "
        "when this magnificent ship set sail on its maiden voyage, only to meet a chilling fate. Brace yourself for some exciting Titanic trivia:"
    )
    st.image("https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/St%C3%B6wer_Titanic.jpg/800px-St%C3%B6wer_Titanic.jpg", caption="RMS Titanic 📸")

    st.subheader("Get Ready for Some Titanic Trivia ℹ:")
    st.markdown(
        "- Meet the Titanic, one of three Olympic-class ocean liners operated by the famous White Star Line. 🚢"
    )
    st.markdown(
        "- Picture this: April 14, 1912, a moonlit night, and suddenly, an iceberg! The Titanic's ill-fated encounter sent her plunging into the icy depths of the North Atlantic Ocean on April 15, 1912. ❄🌊"
    )
    st.markdown(
        "- Hold onto your seats! More than 1,500 brave souls lost their lives in this heart-wrenching disaster, marking it as one of the most tragic peacetime maritime events in history. ☠"
    )
    st.markdown(
        "- The Titanic was a marvel of its time, equipped with advanced safety features like watertight compartments and lifeboats. But, alas, there weren't enough lifeboats for everyone! 🆘🛶"
    )

    st.write(
        "Join us in exploring the incredible history of the Titanic, where adventure meets tragedy, and learn how you would have fared on this unforgettable voyage!"
    )

    st.markdown("# Titanic Survival Prediction 🧑🚀")
    st.write(
        "Welcome to the Titanic Survival Predictor! Let's take a journey into the AI world and predict your fate if you were on the Titanic. Exciting, right? 🚢"
    )

    with st.expander("How Does Our AI Model Work? ✨"):
        st.markdown("# How Does the AI Model Work? 🤖✨")

        st.write(
            "Welcome to the Titanic Survival Prediction! 🚢 These predictions are generated by an Artificial Intelligence model. Our model has an accuracy rate of approximately 80%, and we use the Random Forest classification algorithm to make predictions."
        )

        st.markdown(
            "The Random Forest classification algorithm is a versatile model created by combining numerous decision trees. 🌲🌲🌲 These trees are trained using random data samples and features. The model combines the predictions from these trees to produce results. 🌳🔮"
        )

        st.markdown(
            "The Random Forest algorithm is used to predict the survival of Titanic passengers. The model takes into account passengers' personal information, ticket class, gender, age, and more to make predictions. 🎫👫📆"
        )

        st.markdown(
            "However, please keep in mind that these predictions are merely estimates, and real-life outcomes can be influenced by various factors. While our model has an accuracy rate of approximately 80%, the results are not definitive and are for entertainment purposes only. 🎉🤪"
        )

    st.write(
        "Go ahead and fill out the form, then click the 'Submit' button to see your prediction! 🚀"
    )

    with st.expander("Feeling Adventurous? Fill Out the Form! 🚀",expanded=True):
        st.write(
            "🌟 Ahoy there! Welcome aboard the Titanic adventure! We're setting sail on a journey through time, and you're invited to be a part of it. "
            "Picture yourself as a passenger on the grand RMS Titanic. We'll use our AI magic to predict whether you'd survive this legendary voyage."
        )

        st.write(
            "🕰 But before we embark on this thrilling quest, let's put on our virtual time-traveler hats and gather some essential information."
        )

        st.write(
            "📝 Fear not, it's not just a regular form; it's your ticket to the Titanic experience! Fill in your details like your name, age, gender, and more, "
            "and we'll do the rest. Will you make it to the lifeboats, or will you brave the icy waters of the North Atlantic? Let's uncover the adventure!"
        )

        st.write(
            "🚢 Keep in mind, this is all for fun and the love of adventure. Our AI model will make a prediction, but the real excitement lies in your imagination. Are you ready to set sail?"
        )

        success_control = False

        user_name_holder = ""
        user_surname_holder = ""
        pclass_holder = 1
        age_holder = 0
        sib_holder = 0
        sp_holder = 0
        par_holder = 0
        ch_holder = 0
        sex_holder = 0
        fare_holder = 0
        embarked_holder = 0
        deck_letter_holder = 0

        col1, col2 = st.columns(2)
        user_name = col1.text_input("Your First Name ✍", max_chars=50, placeholder="Enter your first name",value=user_name_holder)
        user_surname = col2.text_input("Your Last Name ✍", max_chars=50, placeholder="Enter your last name",value=user_surname_holder)

        pclass = st.radio("Choose Your Passenger Class 🚢", options=(1, 2, 3),index=pclass_holder-1)

        age = st.slider("Your Age 🎂", min_value=0, max_value=85, value=age_holder)

        sib = st.slider("Number of Siblings Aboard 👨👩👧👦", min_value=0, max_value=5, value=sib_holder)

        sp = st.slider("Number of Spouses Aboard 👩❤👨", min_value=0, max_value=5, value=sp_holder)

        sib_sp = sib + sp

        par = st.slider("Number of Parents Aboard 👪", min_value=0, max_value=5, value=par_holder)

        ch = st.slider("Number of Children Aboard 👶", min_value=0, max_value=5, value=ch_holder)

        parch = par + ch

        num_family = parch + sib_sp + 1

        sex = st.selectbox("Your Gender 👩🦰👨🦱", options=("Female", "Male"), index=sex_holder)

        min_fare = titanic.loc[titanic["Pclass"] == pclass, "Fare"].min()
        max_fare = titanic.loc[titanic["Pclass"] == pclass, "Fare"].max()

        st.write("💰 The range of Fare is calculated based on your Passenger class")

        fare = st.slider(f"💷 Ticket Price in British Pounds (£) between {min_fare} and {max_fare}", min_value=float(min_fare), max_value=float(max_fare), value=float(fare_holder))

        embarked = st.selectbox("Port of Embarkation 🚢", options=("Cherbourg (C)", "Southampton (S)", "Queenstown (Q)"), index=embarked_holder)

        st.write("🚢 The Titanic has decks labeled from A (top) to F (bottom), with 'U' representing unknown decks. The available deck options are calculated based on your chosen passenger class.")

        deck_options = titanic.loc[titanic["Pclass"] == pclass, "Deck_letter"].unique().tolist()
        deck_letter = st.selectbox("Deck Letter 🚢", options=(deck_options), index=deck_letter_holder)

        submit, _, _, _, randomizer = st.columns(5)

        if submit.button("Submit ✅"):
            if age == 0:
                st.warning("Please fill in the required fields ⬆")
            else:
                st.success("Submitted Successfully! 👍")
                success_control = True

        if success_control:
            # Create a dictionary with scalar values and specify an index
            data = {
                'Pclass': [pclass],
                'Age': [age],
                'SibSp': [sib_sp],
                'Parch': [parch],
                'Fare': [fare],
                'NumFamily': [num_family],
                'Sex_female': [0],
                'Sex_male': [0],
                'Embarked_C': [0],
                'Embarked_Q': [0],
                'Embarked_S': [0],
                'Deck_letter_A': [0],
                'Deck_letter_B': [0],
                'Deck_letter_C': [0],
                'Deck_letter_D': [0],
                'Deck_letter_E': [0],
                'Deck_letter_F': [0],
                'Deck_letter_G': [0],
                'Deck_letter_U': [0],
            }

            # Specify an index for the DataFrame
            index = [0]  # You can adjust the index as needed

            df = pd.DataFrame(data, index=index)

            embarked = embarked[0]
            df[f"Embarked_{embarked}"] = 1

            df[f"Sex_{sex.lower()}"] = 1

            df[f"Deck_letter_{deck_letter}"] = 1

            # Load the model from the pickle file
            URI = "https://raw.githubusercontent.com/ahmettalhabektas/Streamlit/master/Titanic_Streamlit/model.pkl"
            response = requests.get(URI)

            if response.status_code == 200:
                model = pickle.loads(response.content)

            # Make predictions using the loaded model
            predictions = model.predict(df)
            # Specify the repository and path to the directories
            repo_owner = "ahmettalhabektas"
            repo_name = "Streamlit"
            alive_directory_path = "Titanic_Streamlit/alive"
            dead_directory_path = "Titanic_Streamlit/dead"

            # Fetch the file lists using the GitHub API
            def fetch_github_file_list(owner, repo, path):
                github_api_url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
                response = requests.get(github_api_url)

                if response.status_code == 200:
                    files = [file['name'] for file in response.json() if file['type'] == 'file']
                    return files
                else:
                    st.warning("Failed to fetch file list from GitHub.")
                    return []

            alive_files = fetch_github_file_list(repo_owner, repo_name, alive_directory_path)
            dead_files = fetch_github_file_list(repo_owner, repo_name, dead_directory_path)

            image_place = st.empty()

            if predictions[0] == 0:
                st.write("🌊 Oh no! It seems you didn't make it to a lifeboat. But remember, it's all in good fun!")
                if dead_files:
                    chosen_dead_file = random.choice(dead_files)
                    image_url = f"https://raw.githubusercontent.com/{repo_owner}/{repo_name}/master/{dead_directory_path}/{chosen_dead_file}"
                    image_place.image(image_url, width=675)
                else:
                    st.warning("No images found in the 'dead' directory.")

            elif predictions[0] == 1:
                st.write("🎉 Congratulations! You've survived the Titanic adventure! Now, imagine the thrilling stories you'd have to tell!")
                if alive_files:
                    chosen_alive_file = random.choice(alive_files)
                    image_url = f"https://raw.githubusercontent.com/{repo_owner}/{repo_name}/master/{alive_directory_path}/{chosen_alive_file}"
                    image_place.image(image_url, width=675)
                else:
                    st.warning("No images found in the 'alive' directory.")

            else:
                st.warning("A problem occurred")

    st.write("I bet you're itching to uncover the secrets hidden in Titanic's data, right? Well, get ready for a thrilling data-driven adventure with some cool graphs!")

    with st.expander("Let's Dive into Data Visualization 📊"):
        st.write(
            "Hold onto your life jackets because we're about to embark on a thrilling adventure through Titanic's data!"
        )

        st.markdown(
            "🎉 **Discover the Titanic's Secrets**: Imagine being Sherlock Holmes on the Titanic! We'll uncover how passenger class, gender, and age played roles in who survived. Get ready to unveil the mysteries!"
        )

        st.markdown(
            "🚀 **Blast Off with Graphs**: Our secret weapon? Powerful graphs! They're like magic windows into the Titanic's history. Buckle up for a rocket ride!"
        )

        st.markdown(
            "💡 **Get Insights**: These visualizations will provide insights that even Jack and Rose from the movie would find fascinating. Are you ready for this data-driven adventure? The Titanic's secrets await!"
        )

        st.write("Hit that 'Create Graphs' button below, and let's set sail!")

        bins = list(range(0, 81, 5))  # Creates bins from 0 to 80 with a step of 5

        # Use pd.cut to categorize the data into the specified bins
        titanic['Age Range'] = pd.cut(titanic['Age'], bins=bins, right=True)

        bins = list(range(0, 300, 20))  # Creates bins from 0 to 80 with a step of 5

        # Use pd.cut to categorize the data into the specified bins
        titanic['Fare Range'] = pd.cut(titanic['Fare'], bins=bins, right=True)

        st.write("If you're curious about the real statistics of the Titanic dataset, let's dive in together.")

        user_hue = st.selectbox("What should we use for color encoding?", options=("None", "Survived", "Passenger Class", "Sex", "Age Range", "Siblings & Spouses", "Parents & Children", "Fare Range", "Port of Embarkation", "Deck Letter"))
        user_x = st.selectbox("Choose a value for the x-axis", options=("Survived", "Passenger Class", "Sex", "Siblings & Spouses", "Parents & Children", "Port of Embarkation", "Deck Letter"))

        if user_hue == "None":
            user_hue = None
        titanic.Survived = titanic.Survived.replace([1, 0], ["Survived 😊", "Not Survived 😥"])
        titanic = titanic.rename(columns={"Pclass": "Passenger Class", "SibSp": "Siblings & Spouses", "Parch": "Parents & Children", "Embarked": "Port of Embarkation","Deck_letter":"Deck Letter"})
        fig = plt.figure()
        plot_df = titanic.copy()
        sns.countplot(data=plot_df, x=user_x, hue=user_hue)
        if st.button("Create Graph"):
            st.pyplot(fig)

    # Contact Section
    st.markdown("# Contact Me 📬")
    st.write(
        "If you have any questions or feedback about the Titanic Survival Predictor, feel free to contact me through the following channels:"
    )
    st.markdown(
        "- 📧 [Email](mailto:atalhabektas@gmail.com)"
    )
    st.markdown(
        "- 🔗 [LinkedIn](https://www.linkedin.com/in/ahmet-talha-bekta%C5%9F-056844216)"
    )
    st.markdown(
        "- 💻 [GitHub](https://github.com/ahmettalhabektas)"
    )
    st.markdown(
        "- 👨💻 [Kaggle](https://www.kaggle.com/ahmettalhabektas)"
    )
    st.markdown(
        "Made with 💖 by [Ahmet Talha Bektaş](https://www.linkedin.com/in/ahmet-talha-bekta%C5%9F-056844216)"
    )
else:
    # Turkish Content
    st.markdown("# Titanik Macerasına Hoş Geldiniz 🚢")
    st.write(
        "Legandary British yolcu gemisi RMS Titanic'e hoş geldiniz! Bu büyüleyici geminin ilginç hikayesine yolculuk ediyoruz. Bize katılın ve bu muhteşem geminin ilk seferine, Nisan 1912'ye geri dönelim, "
        "Ancak maalesef korkunç bir kaderle karşılaşan bu seyahati yaparken heyecan verici Titanik bilgileriyle hazırlıklı olun:"
    )
    st.image("https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/St%C3%B6wer_Titanic.jpg/800px-St%C3%B6wer_Titanic.jpg", caption="RMS Titanic 📸")

    st.subheader("Titanik Bilgileri İçin Hazır Olun ℹ:")
    st.markdown(
        "- Ünlü White Star Line tarafından işletilen üç Olympic sınıfı okyanus gemisinden biri olan Titanik'i tanıyın. 🚢"
    )
    st.markdown(
        "- Şöyle düşünün: 14 Nisan 1912, aydınlık bir gece ve birdenbire bir buzdağı! Titanik'in talihsiz karşılaşması, onu 15 Nisan 1912'de Kuzey Atlantik Okyanusu'nun buz gibi sularına gönderdi. ❄🌊"
    )
    st.markdown(
        "- Koltuklarınıza sıkıca tutun! Bu yürek burkan felakette 1.500'den fazla cesur insan hayatını kaybetti ve bu, tarih boyunca kaydedilmiş en trajik barış zamanı deniz olaylarından biri olarak kabul ediliyor. ☠"
    )
    st.markdown(
        "- Titanik, döneminin bir harikasıydı ve su geçirmez bölmeler ve cankurtaran botlar gibi gelişmiş güvenlik özelliklerine sahipti. Ama ne yazık ki, herkes için yeterli cankurtaran bot yoktu! 🆘🛶"
    )

    st.write(
        "Titanik'in inanılmaz tarihini keşfetmek için bize katılın, macera ve trajedinin buluştuğu bu unutulmaz yolculukta nasıl bir performans sergilediğinizi öğrenin!"
    )

    # Turkish Content
    st.markdown("# Titanic Hayatta Kalma Tahmini 🧑🚀")
    st.write(
        "Titanik Hayatta Kalma Tahminine hoş geldiniz! AI dünyasına bir yolculuk yapalım ve Titanik'te olsaydınız kaderinizi tahmin edelim. Heyecanlı değil mi? 🚢"
    )

    with st.expander("Yapay Zeka modelimiz nasıl çalışıyor? ✨"):
        st.markdown("# Yapay Zeka Modeli Nasıl Çalışıyor? 🤖✨")

        st.write(
            "Titanik Hayatta Kalma Tahminine hoş geldiniz! 🚢 Bu tahminler, bir Yapay Zeka modeli tarafından üretilmektedir. Modelimizin doğruluk oranı yaklaşık %80'dir ve sonuçları tahmin etmek için Random Forest sınıflandırma algoritması kullanıyoruz."
        )

        st.markdown(
            "Random Forest sınıflandırma algoritması, çok sayıda karar ağacının bir araya getirilmesiyle oluşturulan bir ansiklopedik modeldir. 🌲🌲🌲 Bu ağaçlar, rastgele veri örnekleri ve özellikler kullanılarak eğitilir. Model, bu ağaçların tahminlerini bir araya getirerek sonuçları üretir. 🌳🔮"
        )

        st.markdown(
            "Random Forest algoritması, Titanik yolcularının hayatta kalma tahminlerini yapmak için kullanılıyor. Model, yolcuların kişisel bilgileri, bilet sınıfı, cinsiyet, yaş, ve daha fazlasını dikkate alarak tahminlerde bulunur. 🎫👫📆"
        )

        st.markdown(
            "Ancak unutmayın ki bu tahminler sadece bir öngörüdür ve gerçek hayatta çok sayıda faktör etkileyebilir. Modelimizin doğruluk oranı %80 olsa da, sonuçlar kesin değildir ve yalnızca eğlence amaçlıdır. 🎉🤪"
        )

    # Turkish Content
    st.write(
        "Formu doldurun ve tahmininizi görmek için 'Gönder' düğmesine tıklayın! 🚀"
    )

    with st.expander("Macera Dolu Musunuz? Formu Doldurun! 🚀",expanded=True):
        st.write(
            "🌟 Ahoy oraya! Titanik macerasına hoş geldiniz! Zaman yolculuğuna çıkıyoruz ve siz de bu yolculuğun bir parçası olmaya davetlisiniz. "
            "Kendinizi büyük RMS Titanic'in bir yolcusu olarak hayal edin. AI sihirlerimizi kullanarak bu efsanevi yolculukta hayatta kalıp kalmayacağınızı tahmin edeceğiz."
        )

        st.write(
            "🕰 Ancak bu heyecanlı maceraya başlamadan önce sanal zaman yolcusu şapkalarımızı takalım ve bazı temel bilgileri toplayalım."
        )

        st.write(
            "📝 Endişelenmeyin, bu sıradan bir form değil; bu, Titanik deneyiminiz için bir bilet! İsminiz, yaşınız, cinsiyetiniz ve daha fazlası gibi detayları doldurun, "
            "gerisini biz hallederiz. Can yeleklerine mi ulaşacaksınız, yoksa Kuzey Atlantik'in buzlu sularına cesurca mı meydan okuyacaksınız? Macerayı keşfetmeye hazır mısınız!"
        )

        st.write(
            "🚢 Unutmayın, bu tamamen eğlence ve macera aşkı içindir. AI modelimiz bir tahmin yapacak, ancak gerçek heyecan hayal gücünüzde yatıyor. Hazır mısınız denize açılmaya?"
        )

        user_name_holder = ""
        user_surname_holder = ""
        pclass_holder = 1
        age_holder = 0
        sib_holder = 0
        sp_holder = 0
        par_holder = 0
        ch_holder = 0
        sex_holder = 0
        fare_holder = 0
        embarked_holder = 0
        deck_letter_holder = 0

        success_control = False
        col1, col2 = st.columns(2)
        user_name = col1.text_input("Adınızı Girin ✍", max_chars=50, placeholder="Adınızı girin",value=user_name_holder)
        user_surname = col2.text_input("Soyadınızı Girin ✍", max_chars=50, placeholder="Soyadınızı girin",value=user_surname_holder)

        pclass = st.radio("Yolcu Sınıfınızı Seçin 🚢", options=(1, 2, 3), index=pclass_holder-1)

        age = st.slider("Yaşınızı Girin 🎂", min_value=0, max_value=85, value=age_holder)

        sib = st.slider("Gemideki Kardeşler Sayısı 👨👩👧👦", min_value=0, max_value=5, value=sib_holder)
        sp = st.slider("Gemideki Eşler Sayısı 👩❤👨", min_value=0, max_value=5, value=sp_holder)

        sib_sp = sib + sp

        par = st.slider("Gemideki Ebeveynler Sayısı 👪", min_value=0, max_value=5, value=par_holder)
        ch = st.slider("Gemideki Çocuklar Sayısı 👶", min_value=0, max_value=5, value=ch_holder)

        parch = par + ch

        num_family = parch + sib_sp + 1

        sex = st.selectbox("Cinsiyetiniz 👩🦰👨🦱", options=("Kadın", "Erkek"), index=sex_holder)

        min_fare = titanic.loc[titanic["Pclass"] == pclass, "Fare"].min()
        max_fare = titanic.loc[titanic["Pclass"] == pclass, "Fare"].max()

        st.write("💰 Bilet fiyatı aralığı, Yolcu Sınıfınıza göre hesaplanır")

        fare = st.slider(f"💷 İngiliz Sterlini (£) cinsinden Bilet Fiyatı, {min_fare} ile {max_fare} arasında", min_value=min_fare, max_value=max_fare, value=float(fare_holder))

        embarked = st.selectbox("Biniş Limanı 🚢", options=("Cherbourg (C)", "Southampton (S)", "Queenstown (Q)"), index=embarked_holder)

        st.write("🚢 Titanik, A (en üst) ile F (en alt) arasında numaralandırılmış güvertelere sahipti ve 'U'  güverteleri bilinmeyen yolcuları temsil eder. Kullanılabilir güverte seçenekleri, seçtiğiniz yolcu sınıfına göre hesaplanır.")

        deck_options = titanic.loc[titanic["Pclass"] == pclass, "Deck_letter"].unique().tolist()
        deck_letter = st.selectbox("Güverte Harfi 🚢", options=(deck_options), index=deck_letter_holder)

        submit, _, _, _, randomizer = st.columns(5)

        if submit.button("Gönder ✅"):
            if age == 0:
                st.warning("Lütfen gerekli alanları doldurun ⬆")
            else:
                st.success("Başarıyla Gönderildi! 👍")
                success_control = True

        if success_control:
            # Bir dizi skaler değeri içeren bir sözlük oluşturun ve bir indeks belirtin
            veri = {
                'Pclass': [pclass],
                'Age': [age],
                'SibSp': [sib_sp],
                'Parch': [parch],
                'Fare': [fare],
                'NumFamily': [num_family],
                'Sex_female': [0],
                'Sex_male': [0],
                'Embarked_C': [0],
                'Embarked_Q': [0],
                'Embarked_S': [0],
                'Deck_letter_A': [0],
                'Deck_letter_B': [0],
                'Deck_letter_C': [0],
                'Deck_letter_D': [0],
                'Deck_letter_E': [0],
                'Deck_letter_F': [0],
                'Deck_letter_G': [0],
                'Deck_letter_U': [0],
            }

            # DataFrame için bir indeksi belirtin
            indeks = [0]  # İstenirse indeksi ayarlayabilirsiniz

            df = pd.DataFrame(veri, index=indeks)

            embarked = embarked[0]
            df[f"Embarked_{embarked}"] = 1

            sex=sex.replace("Erkek","Male")
            sex=sex.replace("Kadın","Female")

            df[f"Sex_{sex.lower()}"] = 1

            df[f"Deck_letter_{deck_letter}"] = 1

            # Load the model from the pickle file
            URI = "https://raw.githubusercontent.com/ahmettalhabektas/Streamlit/master/Titanic_Streamlit/model.pkl"
            response = requests.get(URI)

            if response.status_code == 200:
                model = pickle.loads(response.content)
            # Yüklenmiş modeli kullanarak tahminler yapın
            tahminler = model.predict(df)

            image_place = st.empty()

            # Specify the repository and path to the directories
            repo_owner = "ahmettalhabektas"
            repo_name = "Streamlit"
            alive_directory_path = "Titanic_Streamlit/alive"
            dead_directory_path = "Titanic_Streamlit/dead"

            # Fetch the file lists using the GitHub API
            def fetch_github_file_list(owner, repo, path):
                github_api_url = f"https://api.github.com/repos/{owner}/{repo}/contents/{path}"
                response = requests.get(github_api_url)

                if response.status_code == 200:
                    files = [file['name'] for file in response.json() if file['type'] == 'file']
                    return files
                else:
                    st.warning("Failed to fetch file list from GitHub.")
                    return []

            alive_files = fetch_github_file_list(repo_owner, repo_name, alive_directory_path)
            dead_files = fetch_github_file_list(repo_owner, repo_name, dead_directory_path)

            image_place = st.empty()

            if tahminler[0] == 0:
                st.write("🌊 Maalesef! Görünüşe göre cankurtaran botuna yetişemediniz. Ancak unutmayın, hepsi eğlence amaçlı!")
                if dead_files:
                    chosen_dead_file = random.choice(dead_files)
                    image_url = f"https://raw.githubusercontent.com/{repo_owner}/{repo_name}/master/{dead_directory_path}/{chosen_dead_file}"
                    image_place.image(image_url, width=675)
                else:
                    st.warning("'dead' klasöründe bir dosya bulunamadı.")

            elif tahminler[0] == 1:
                st.write("🎉 Tebrikler! Titanik macerasını sağ salim atlattınız! Şimdi, anlatacak heyecan verici hikayeler hayal edin!")
                if alive_files:
                    chosen_alive_file = random.choice(alive_files)
                    image_url = f"https://raw.githubusercontent.com/{repo_owner}/{repo_name}/master/{alive_directory_path}/{chosen_alive_file}"
                    image_place.image(image_url, width=675)
                else:
                    st.warning("'alive' klasöründe bir dosya bulunamadı.")

            else:
                st.warning("Bir problem oluştu lütfen sağ üsteki sekmeden about kısmından benimle iletişime geçin!")

    st.write("Tahmin ediyorum ki Titanic verilerinde gizlenen sırları açığa çıkarmak için sabırsızlanıyorsunuz, öyle değil mi? İşte harika grafiklerle dolu heyecan verici bir veri yolculuğuna hazırlanın!")

    with st.expander("Veri Görselleştirmeye Hazır Mısınız? 📊"):
        st.write(
            "Can yeleklerinizi sıkıca tutun, çünkü Titanic'in verileriyle heyecan dolu bir yolculuğa çıkmak üzereyiz!"
        )

        st.markdown(
            "🎉 **Titanik'in Sırlarını Keşfet**: Titanic'te Sherlock Holmes gibi hissedin! Yolcu sınıfı, cinsiyet ve yaşın kimin hayatta kalacağına nasıl etki ettiğini keşfedeceğiz. Gizemleri açığa çıkarmak için hazır olun!"
        )

        st.markdown(
            "🚀 **Grafiklerle Patlama Yapın**: Gizli silahımız? Güçlü grafikler! Onlar, Titanic'in tarihine sihirli pencereler gibidir. Koltuklarınızı sıkıca bağlayın, çünkü bir roket yolculuğuna çıkıyoruz!"
        )

        st.markdown(
            "💡 **Görüşler Elde Edin**: Bu görselleştirmeler, Titanic'in tarihini bile ilginç bulacak Jack ve Rose gibi insanlara bile ilginç gelecek bilgiler sağlayacak. Bu veri yolculuğuna hazır mısınız? Titanic'in sırları sizi bekliyor!"
        )

        st.write("'Grafik Oluştur' düğmesine tıklayın ve yolculuğa başlayalım!")

        bins = list(range(0, 81, 5))  # 0 ile 80 arasında 5 birimlik aralıklarla bölmeler oluşturur

        # pd.cut kullanarak veriyi belirtilen bölmelere kategorize eder
        titanic['Yaş Aralığı'] = pd.cut(titanic['Age'], bins=bins, right=True)

        bins = list(range(0, 300, 20))  # 0 ile 300 arasında 20 birimlik aralıklarla bölmeler oluşturur

        # pd.cut kullanarak veriyi belirtilen bölmelere kategorize eder
        titanic['Bilet Fiyatı Aralığı'] = pd.cut(titanic['Fare'], bins=bins, right=True)

        st.write("Eğer Titanik veri kümesinin gerçek istatistikleri sizi meraklandırıyorsa, birlikte dalalım.")

        user_hue = st.selectbox("Renk kodlaması için ne kullanmalıyız?", options=("Yok", "Kurtulanlar", "Yolcu Sınıfı", "Cinsiyet", "Yaş Aralığı", "Kardeşler & Eşler", "Anne & Babalar", "Bilet Fiyatı Aralığı", "Biniş Limanı", "Gemi Katı Harfi"))
        user_x = st.selectbox("X ekseni için bir değer seçin", options=("Kurtulanlar", "Yolcu Sınıfı", "Cinsiyet", "Kardeşler & Eşler", "Ebeveynler & Çocuklar", "Biniş Limanı", "Gemi Katı Harfi"))

        if user_hue == "Yok":
            user_hue = None

        titanic.Survived = titanic.Survived.replace([1, 0], ["Kurtulanlar 😊", "Kurtulamayanlar 😥"])
        titanic.Sex = titanic.Sex.replace(["male","female"], ["Erkek", "Kadın"])

        titanic = titanic.rename(columns={"Survived":"Kurtulanlar","Pclass": "Yolcu Sınıfı", "SibSp": "Kardeşler & Eşler", "Parch": "Ebeveynler & Çocuklar", "Embarked": "Biniş Limanı","Deck_letter":"Gemi Katı Harfi","Sex":"Cinsiyet"})
        fig = plt.figure()
        plot_df = titanic.copy()
        sns.countplot(data=plot_df, x=user_x, hue=user_hue)
        if st.button("Grafik Oluştur"):
            st.pyplot(fig)

    # İletişim Bölümü
    st.markdown("# Benimle İletişime Geçin 📬")
    st.write(
        "Titanic Hayatta Kalma Tahmincisi hakkında herhangi bir sorunuz veya geri bildiriminiz varsa, aşağıdaki iletişim kanalları üzerinden benimle iletişime geçmekten çekinmeyin:"
    )
    st.markdown(
        "- 📧 [Email](mailto:atalhabektas@gmail.com)"
    )
    st.markdown(
        "- 🔗 [LinkedIn](https://www.linkedin.com/in/ahmet-talha-bekta%C5%9F-056844216)"
    )
    st.markdown(
        "- 💻 [GitHub](https://github.com/ahmettalhabektas)"
    )
    st.markdown(
        "- 👨💻 [Kaggle](https://www.kaggle.com/ahmettalhabektas)"
    )
    st.markdown(
        "Ahmet Talha Bektaş tarafından 💖 ile oluşturulmuştur: "
    )


import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

class RNN_v4(nn.Module):

    def __init__(self, num_layers, feature_size, embedding_size, hidden_size, taget_size):
        super(RNN_v4, self).__init__()
        self.num_layers = num_layers
        self.feature_size = feature_size
        self.embedding_size = embedding_size
        self.hidden_size = hidden_size
        self.taget_size = taget_size

        self.fc1 = nn.Linear(feature_size, embedding_size)
        self.fc2 = nn.Linear(embedding_size, embedding_size)

        self.rnn = nn.GRU(embedding_size, hidden_size, num_layers)
        self.fc_out = nn.Linear(hidden_size, taget_size)

    def forward(self, inputs):
        output = F.relu(self.fc1(inputs))
        output = F.relu(self.fc2(output))

        output, _ = self.rnn(output)
        output = self.fc_out(output) # map hidden dim to output dim
        # output[:,:,0:4] = torch.cumsum(output[:,:,0:4], dim=0) # cdf sum of probability
        output[:,:,0:4] = torch.sigmoid(output[:,:,0:4])

        return output


import requests
from bs4 import BeautifulSoup as BS

class GetAllUrl(object):

    header={
    "Accept": "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-cn",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "keep-alive",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Safari/605.1.15"
    }
    def __init__(self,h=None):
        if(h is not None):
            self.header=h

    def getAllPagesUrl(self,pagesUrlsList,bs_s, header=header):
        #对于多页的情况，获取每一页的url
        bs_s_url = bs_s.find(class_="pager").find_all("a")
        for u in bs_s_url:
            if u["href"] not in pagesUrlsList:
                pagesUrlsList.append(u["href"])
        return pagesUrlsList

    def getAllUrlListInPages(self,pagesUrlsList, header=header):
        #获取页面表内，全部的文章链接，返回一个列表
        contextUrlsList = []
        for pages in pagesUrlsList:
            r_s = requests.get(pages, header)
            bs_s = BS(r_s.text,"lxml")
            urls = bs_s.find_all(class_="entrylistItemTitle")

            for u in urls:
                if u["href"] not in contextUrlsList:
                    contextUrlsList.append(u["href"])
        return contextUrlsList

    def getAllUrlDicInPages(self,pagesUrlsList, header=header):
        #获取页面表内，全部的文章链接，返回一个字典，键为文章标题，值为文章链接
        contextUrlsDic = {}
        for pages in pagesUrlsList:
            r_s = requests.get(pages, header)
            bs_s = BS(r_s.text,"lxml")
            urls = bs_s.find_all(class_="entrylistItemTitle")

            for u in urls:
                if u.find("span").string not in contextUrlsDic:
                    contextUrlsDic[u.find("span").string]=u["href"]
        return contextUrlsDic

    def getUrlsList(self,url,header=header):
        #获取url系列页的所有文章链接，返回一个列表
        pagesUrlsList = []
        pagesUrlsList.append(url)
        r_s = requests.get(url, header)
        bs_s = BS(r_s.text,"lxml")
        pagesUrlsList = self.getAllPagesUrl(pagesUrlsList,bs_s, header)
        contextUrlsList = self.getAllUrlListInPages(pagesUrlsList,header)
        return contextUrlsList

    def getUrlsDic(self,url,header=header):
        #获取url系列页的所有文章链接，返回一个字典，键为文章标题，值为文章链接
        pagesUrlsList = []
        pagesUrlsList.append(url)
        r_s = requests.get(url, header)
        bs_s = BS(r_s.text,"lxml")
        pagesUrlsList = self.getAllPagesUrl(pagesUrlsList,bs_s, header)
        contextUrlsList = self.getAllUrlDicInPages(pagesUrlsList,header)
        return contextUrlsList
if __name__ == '__main__':
    url = "https://www.cnblogs.com/ghosteq/category/2100349.html?page=1"
    header={
    "Accept": "application/json,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-cn",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "keep-alive",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Safari/605.1.15"
    }
    ga = GetAllUrl(header)
    contextUrlsList = ga.getUrlsList(url)
    contextUrlsDic = ga.getUrlsDic(url)
    print(contextUrlsList)
    print(len(contextUrlsList))
    print(contextUrlsDic)
    print(len(contextUrlsDic))


from datetime import date, datetime, time, timedelta
from typing import List, Optional, Callable

import plotly.graph_objects as go
from colour import Color

from input_reading import BusLeg
from others import Employee

class VisualInterval:
    """Container for the data associated with an interval for visualisation."""

    def __init__(self, start, end, pos: int,  hover: Optional[str], text: Optional[str]) -> None:
        """Set the content.

        Start and end might be integers, datetime, or datetime strings.
        :param start: Start of the interval
        :param end: End of the interval
        :param hover: Text to show on hover, supports plotly formatting
        :param text: Text to show in the center of the interval, supports plotly formatting
        """
        self.start = start
        self.end = end
        self.pos = pos
        self.hover = hover
        self.text = text

class IntervalVisualizer:
    """Factory for creating plotly figures for interval visualisation."""

    AXIS_VAL = 0
    AXIS_HOURS = 1
    AXIS_DAYS = 2
    AXIS_DATE = 3

    def __init__(self, axis: int = AXIS_VAL, axis_delta: timedelta = timedelta(minutes=1), axis_date: date = None) -> \
            None:
        """Initialize the visualizer.

        :param axis: Scale on the time axis, one of AXIS_VAL for integers, AXIS_HOURS for HH:MM, AXIS_DAYS for 'dd hh',
        or AXIS_DATE for date and time
        :param axis_delta: Time granularity in the given intervals
        :param axis_date: Start date for AXIS_DATE, otherwise ignored
        """
        self.height = .4
        self.axis = axis
        self.axis_format = None
        self.hover_format = '%H:%M'
        if axis == self.AXIS_HOURS:
            self.axis_format = '%H:%M'
            axis_date = date(2020, 1, 1)
        elif axis == self.AXIS_DAYS:
            self.axis_format = '%-jd %Hh'
            self.hover_format = '%jd %H:%M'
            axis_date = date(2020, 1, 1)
        elif axis == self.AXIS_DATE:
            self.hover_format = '%Y-%m-%d %H:%M'
        if axis_delta < timedelta(minutes=1):
            self.hover_format += ':%S'
        self.axis_delta = axis_delta
        self.axis_start = datetime.combine(axis_date if axis_date is not None else date.today(), time())

    def __build_hover(self, interval: BusLeg, hover: Optional[str]) -> Optional[str]:
        """Build the hover text from the interval based on the template."""

        if hover is None:
            return None

        index = 0
        while '%' in hover[index:]:

            pos = hover.find('%', index)
            if hover[pos + 1] == '%':
                index += 2
                continue
            end_pos = hover.find('}', index)

            attr = hover[pos + 2:end_pos]
            replace = getattr(interval, hover[pos + 2:end_pos], None)
            if self.axis != self.AXIS_VAL and (attr == 'start' or attr == 'end'):
                replace = (self.axis_start + self.axis_delta * replace).strftime(self.hover_format)
                if self.axis == self.AXIS_DAYS:
                    replace = replace.lstrip('0')
            else:
                replace = str(replace)

            hover = hover.replace(hover[pos:end_pos + 1], replace)

        hover = hover.replace('%%', '%')
        return hover

    @staticmethod
    def __contrast_color(col: Color) -> Color:
        """Return the appropriate text color (black or white) for the given background color."""
        if 0.299 * col.red + 0.587 * col.green + 0.114 * col.blue > 0.5:
            return Color('black')
        else:
            return Color('white')

    def create(self, intervals: List[Employee], title: str, type_attr: Optional[str] = None,
               hovertext: Callable[[BusLeg], str] = lambda x: None, text_attr: Optional[str] = None,
               hidden: Optional[List[str]] = None) -> go.Figure:
        """Create the figure for the given interval sequences.

        :param intervals: List of sequences, each sequence corresponds to one row with the attribute 'name' on the axis
        :param title: Title of the figure
        :param type_attr: BusLeg attribute for grouping the intervals, each type corresponds to a legend entry and a
        (most likely) unique color
        :param hovertext: Function returing the hover text template for each interval, allows plotly formatting like
        ... or
 and using interval attributes with %{attr}, %{start} and %{end} are formatted according to
        self.axis, no hover text if None is returned
        :param text_attr: BusLeg attribute to show as text in the center of each interval, formatted like hovertext
        :param hidden: List of types that are initially hidden
        """

        types = {}
        if hidden is None:
            hidden = []

        for i, sequence in enumerate(intervals):
            for interval in sequence:
                t = None
                if type_attr is not None:
                    t = getattr(interval, type_attr, None)
                if t not in types:
                    types[t] = []
                text = None
                if text_attr is not None:
                    text = getattr(interval, text_attr, None)
                if self.axis == 0:
                    types[t].append(VisualInterval(interval.start,
                                                   interval.end,
                                                   i,
                                                   self.__build_hover(interval, hovertext(interval)),
                                                   text))
                else:
                    types[t].append(VisualInterval(self.axis_start + self.axis_delta * interval.start,
                                                   self.axis_start + self.axis_delta * interval.end,
                                                   i,
                                                   self.__build_hover(interval, hovertext(interval)),
                                                   text))

        data = []
        count = 0
        for t, values in types.items():
            text = []
            text_x = []
            text_y = []
            first = True
            col = Color(pick_for=t, pick_key=None)
            text_col = self.__contrast_color(col)
            for interval in values:
                data.append(go.Scatter(fill='toself',
                                       hoverinfo='none' if interval.hover is None else 'text',
                                       hoverlabel=go.scatter.Hoverlabel(bgcolor=col.hex,
                                                                        font=dict(color=text_col.hex)),
                                       hoveron='fills+points',
                                       legendgroup=t,
                                       line=dict(color=col.hex),
                                       mode='lines',
                                       name=t,
                                       showlegend=first,
                                       text=interval.hover,
                                       visible='legendonly' if t in hidden else True,
                                       x=[interval.start, interval.end, interval.end, interval.start, interval.start],
                                       y=[interval.pos - self.height, interval.pos - self.height,
                                          interval.pos + self.height, interval.pos + self.height,
                                          interval.pos - self.height]))
                first = False
                if interval.text is not None:
                    text.append(interval.text)
                    text_x.append(interval.start + (interval.end - interval.start) / 2)
                    text_y.append(interval.pos)
            data.append(go.Scatter(hoverinfo='skip', legendgroup=t, mode='text', showlegend=False, text=text,
                                   textfont=dict(color=text_col.hex), x=text_x, y=text_y))
            count += 1

        fig = go.Figure(
            data=data,
            layout=go.Layout(
                hoverdistance=3,
                title=go.layout.Title(text=title),
                xaxis=go.layout.XAxis(showspikes=True, spikedash='solid', spikemode='across', spikethickness=1),
                yaxis=go.layout.YAxis(
                    autorange='reversed',
                    ticktext=list(map(lambda v: v.name, intervals)),
                    tickvals=list(range(len(intervals))),
                    zeroline=False
                )
            )
        )

        if self.axis_format is not None:
            fig.update_xaxes(tickformat=self.axis_format)

        return fig


from datetime import datetime
from enum import Enum

class RideType(Enum):
    short = 15
    medium = 40
    long = -1

class RidePrice(Enum):
    short = 5.5
    medium = 12
    long = 23

class RavKav:
    def __init__(self, holder_id: str, holder_name: str, balance: float = 0):
        self.__holder_id = holder_id
        self.__holder_name = holder_name
        self._set_balance(balance)

        self.__dates_log: dict[datetime, int] = {}
        self.__ride_type_log: dict[RideType, int] = {}

    @property
    def get_holder_id(self) -> str:
        return self.__holder_id

    @property
    def get_holder_name(self) -> str:
        return self.__holder_name

    @property
    def get_balance(self) -> float:
        return self.__balance

    def _set_balance(self, value) -> None:
        if value < 0:
            raise ValueError
        self.__balance = value

    def top_up(self, amount: float) -> None:
        if amount < 0:
            raise ValueError
        self._set_balance(amount + self.__balance)

    def _use_balance(self, amount: float) -> None:
        if self.__balance - amount < 0:
            raise ValueError
        self._set_balance(self.__balance - amount)

    def ride(self, ride_km: int, date: datetime.date) -> None:
        if ride_km <= RideType.short.value:
            self._use_balance(RidePrice.short.value)
            self._update_num_rides_by_type(RideType.short)
        elif ride_km <= RideType.medium.value:
            self._use_balance(RidePrice.medium.value)
            self._update_num_rides_by_type(RideType.medium)
        else:
            self._use_balance(RidePrice.long.value)
            self._update_num_rides_by_type(RideType.long)
        self._update_num_rides_by_date(date)

    def get_num_rides_by_date(self, date: datetime.date) -> int:
        if date not in self.__dates_log:
            raise KeyError
        return self.__dates_log[date]

    def _update_num_rides_by_date(self, date: datetime.date) -> None:
        if date not in self.__dates_log:
            self.__dates_log[date] = 0
        self.__dates_log[date] += 1

    def get_num_rides_by_type(self, ride_type: RideType) -> int:
        if ride_type not in self.__ride_type_log:
            raise KeyError
        return self.__ride_type_log[ride_type]

    def _update_num_rides_by_type(self, ride_type: RideType) -> None:
        if ride_type not in self.__ride_type_log:
            self.__ride_type_log[ride_type] = 0
        self.__ride_type_log[ride_type] += 1

    def display_num_rides_by_dates_log(self) -> None:
        print("Rides by Date")
        for k, v in self.__dates_log.items():
            print(f"Date: {k}")
            print(f"\tNumber of rides: {v}")
        return

    def display_num_rides_by_type_log(self) -> None:
        print("Rides by Type")
        for k, v in self.__ride_type_log.items():
            print(f"Type: {k.name}")
            print(f"\tNumber of rides: {v}")
        return

    def __str__(self):
        to_str = f"<RavKav: id={self.__holder_id}>\n" \
                 f"Name: {self.__holder_name}\n" \
                 f"Balance: {self.__balance}"
        return to_str

    def __repr__(self):
        return f"<RavKav: id={self.__holder_id}>"


# Stdlib imports
# Core Django imports
from django.urls import path

# Third-party app imports
# Imports from my apps
from .views import (
    change_campaign,
    data_all_stations,
    data_this_station_margin,
    disable_all_stations,
    download_config,
    enable_all_stations,
    station_campaign_detail_list_view,
    station_confirm_delete_view,
    station_create_view,
    station_detail_list_view,
    station_detail_view,
    station_list_view,
    station_redirect_view,
    station_update_view,
)

app_name = "stations"
urlpatterns = [
    path("~redirect/", view=station_redirect_view, name="redirect"),
    path("~list/", view=station_list_view, name="list"),
    path("~add/", view=station_create_view, name="create"),
    path("~disable_all_stations/", disable_all_stations, name="disable"),
    path("~download_config/", download_config, name="download"),
    path("~enable_all_stations/", enable_all_stations, name="enable"),
    path("@<slug>/", view=station_detail_list_view, name="detail_list"),
    path(
        "#<pk>/@<slug>",
        view=station_campaign_detail_list_view,
        name="campaign_detail_list",
    ),
    # path("#<slug>/", view=station_list2_view, name="detail"),
    path("~detail/<slug>/", view=station_detail_view, name="detail"),
    path("~update/<slug>/", view=station_update_view, name="update"),
    path("~delete/<slug>/", view=station_confirm_delete_view, name="delete"),
    #
    path("ajax/data_this_margin/<slug>/", data_this_station_margin, name="this_margin"),
    path("ajax/data_all_stations/", data_all_stations, name="all_stations"),
    path("ajax/change_campaign/", change_campaign, name="change_campaign"),
]


# logging_utils.py
from __future__ import print_function
import logging
import sys
import time

def logging_config(name, logfile=None):
	"""
	Configure stream and file output logging.
	If we don't provide a logfile (discouraged) then we don't do
	file logging.
	args:
		- name (str): The name of the module from which we log.
	kwargs:
		- logfile (str): The name of the logfile to use for file logging.
	"""
	logger = logging.getLogger(name)
	logger.propagate = False
	logger.setLevel(logging.INFO)
	logging.Formatter.converter = time.gmtime
	formatter_file = logging.Formatter('%(levelname)s - %(asctime)s - %(name)s - %(message)s')
	formatter = logging.Formatter('%(levelname)s - %(name)s - %(message)s')

	# configure stream logging (the output to stdout)
	sh = logging.StreamHandler(sys.stdout)
	sh.setLevel(logging.INFO)
	sh.setFormatter(formatter)

	logger.addHandler(sh)

	# if we have a logfile, configure file logging.
	if logfile:
		fh = logging.FileHandler(logfile)
		fh.setLevel(logging.INFO)
		fh.setFormatter(formatter_file)
		logger.addHandler(fh)

	return logger


##Luke Meyers 5/4/22
# Trying to create a function oriented program out of object oriented package lol

import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.patches as ptch

'''
fig = plt.figure()
ax = fig.add_subplot()

ax.plot([0,1,2,3],[0,1,2,3])
'''

def makeFig(figname='ax'):
    '''generates a subplot with figname and plots arbitary data on it'''
    fig = plt.figure()
    locals()[figname] = fig.add_subplot()
    locals()[figname].plot([0,1],[0,1])

'''
x = 'ax'
globals()[x] = fig.add_subplot()
globals()[x].plot([0,1],[0,1])
'''
'''Ok so basically,globals()[varname] allows you to retrieve the content of
varname and then you can set it as a name of somthing
and you can reference it as much as you. Use globals for whole
program and locals for functions.'''

makeFig('lol')

plt.show()
plt.pause(15)
plt.close()


from typing import (
    Type,
)
from django.http import Http404
from rest_framework.generics import (
    GenericAPIView,
    RetrieveUpdateAPIView,
)
from rest_framework.serializers import (
    Serializer,
)
from rest_framework.viewsets import ModelViewSet
from rest_framework.exceptions import ValidationError, MethodNotAllowed
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.status import HTTP_201_CREATED
from rest_auth.registration.views import VerifyEmailView as VerifyEmailViewBase

from .models import (
    Student,
    Teacher,
    Group,
    GROUP_ADMINS,
    UnregisteredUser,
)
from .serializers import (
    ProfileSerializer,
    StudentSerializer,
    TeacherSerializer,
    UnregisteredStudentSerializer,
    UnregisteredTeacherCreateSerializer,
    GroupBasicSerializer,
    GroupCreateSerializer,
    GroupSerializer,
    RegistrationCodeCheckSerializer,
)
from .permissions import (
    IsAuthenticated,
    IsTeacher,
    IsAdmin,
)

class ProfileView(RetrieveUpdateAPIView):
    serializer_class = ProfileSerializer
    permission_classes = [IsAuthenticated]

    def get_object(self):
        return self.request.user.profile

class RegistrableModelViewSet(ModelViewSet):
    serializer_class_registered: Type[Serializer]
    serializer_class_unregistered: Type[Serializer]

    def get_queryset_registered(self):
        raise NotImplementedError

    def get_queryset_unregistered(self):
        raise NotImplementedError

    def is_registered(self) -> bool:
        pk = self.kwargs['pk']
        return pk[0] != 'c'

    def get_queryset(self):
        # django rest framework call this with rendering list without pk
        if 'pk' in self.kwargs and not self.is_registered():
            return self.get_queryset_unregistered()
        return self.get_queryset_registered()

    def get_serializer(self, *args, **kwargs):
        # django rest framework call this with rendering list without pk
        if 'pk' in self.kwargs and not self.is_registered():
            return self.get_serializer_unregistered(*args, **kwargs)
        return self.get_serializer_registered(*args, **kwargs)

    def get_serializer_registered(self, *args, **kwargs):
        kwargs['context'] = self.get_serializer_context()
        return self.serializer_class_registered(*args, **kwargs)

    def get_serializer_unregistered(self, *args, **kwargs):
        kwargs['context'] = self.get_serializer_context()
        return self.serializer_class_unregistered(*args, **kwargs)

    def list(self, request: Request, **kwargs) -> Response:
        registered_queryset = self.get_queryset_registered()
        registered = self.get_serializer_registered(registered_queryset,
                                                    many=True).data
        unregistered_queryset = self.get_queryset_unregistered()
        unregistered = self.get_serializer_unregistered(unregistered_queryset,
                                                        many=True).data
        return Response(registered + unregistered)

    def create(self, request: Request, **kwargs) -> Response:
        serializer = self.get_serializer_unregistered(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=HTTP_201_CREATED)

class StudentViewSet(RegistrableModelViewSet):
    permission_classes = [IsTeacher]
    serializer_class_registered = StudentSerializer
    serializer_class_unregistered = UnregisteredStudentSerializer

    def get_queryset_registered(self):
        user = self.request.user
        queryset = Student.objects
        if user.is_superuser:
            return queryset
        return queryset.filter(group__teacher__user=user) \
            .exclude(group__name=GROUP_ADMINS)

    def get_queryset_unregistered(self):
        user = self.request.user
        queryset = UnregisteredUser.objects.filter(is_staff=False)
        if user.is_superuser:
            return queryset
        return queryset.filter(group__teacher__user=user)\
            .exclude(group__name=GROUP_ADMINS)

class TeacherViewSet(RegistrableModelViewSet):
    permission_classes = [IsAdmin]
    serializer_class_registered = TeacherSerializer
    serializer_class_unregistered = UnregisteredTeacherCreateSerializer

    def get_queryset_registered(self):
        return Teacher.objects

    def get_queryset_unregistered(self):
        return UnregisteredUser.objects.filter(is_staff=True)

class GroupViewSet(ModelViewSet):
    permission_classes = [IsTeacher]
    lookup_field = 'name'
    lookup_value_regex = '.+'
    serializer_classes = {
        'list': GroupBasicSerializer,
        'create': GroupCreateSerializer,
        'retrieve': GroupSerializer,
        'update': GroupBasicSerializer,
        'partial_update': GroupBasicSerializer,
        'destroy': GroupBasicSerializer,
    }

    def get_serializer_class(self):
        return self.serializer_classes[self.action]

    def get_queryset(self):
        user = self.request.user
        queryset = Group.objects
        if self.request.user.is_superuser:
            return queryset
        return queryset.filter(teacher__user=user).exclude(name=GROUP_ADMINS)

    def destroy(self, *args, **kwargs) -> Response:
        group = self.get_object()
        if group.students.exists() or group.unregistered_students.exists():
            raise ValidationError({
                'number_of_students': [
                    'Cannot delete non-empty group.',
                ],
            })
        return super().destroy(*args, **kwargs)

class RegistrationCodeCheckView(GenericAPIView):
    serializer_class = RegistrationCodeCheckSerializer

    def post(self, request: Request) -> Response:
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        return Response({
            'detail': 'Registration code is valid.',
        })

class EmailVerificationKeyCheckView(VerifyEmailViewBase):
    def post(self, request: Request, **kwargs) -> Response:
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.kwargs['key'] = serializer.validated_data['key']
        try:
            self.get_object()
        except Http404:
            raise ValidationError({
                'key': ['Invalid verification key.']
            })
        return Response({
            'detail': 'Verification key is valid.',
        })

class VerifyEmailView(VerifyEmailViewBase):
    def get(self, request: Request) -> Response:
        raise MethodNotAllowed(request.method)

    def post(self, request: Request, *args, **kwargs) -> Response:
        try:
            super().post(request, *args, **kwargs)
        except Http404:
            raise ValidationError({
                'key': ['Invalid verification key.']
            })
        return Response({
            'detail': 'Email verified.'
        })