import math
import logging
from functools import partial
from collections import OrderedDict
from copy import deepcopy

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn import Softmax
from timm.data import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD, IMAGENET_INCEPTION_MEAN, IMAGENET_INCEPTION_STD
from timm.models.helpers import build_model_with_cfg, named_apply, adapt_input_conv
from timm.models.layers import PatchEmbed, Mlp, DropPath, trunc_normal_, lecun_normal_
from timm.models.registry import register_model
from torch.nn.init import _calculate_fan_in_and_fan_out
import math
import warnings
from timm.models.layers.helpers import to_2tuple

_logger = logging.getLogger(__name__)

class Upsample_eval(torch.nn.Module):
    def __init__(self, type="Upsample", scale=2):
        super(Upsample_eval, self).__init__()
        if type == "Upsample":
            self.up = nn.Upsample(scale_factor=scale)
        elif type == "PixelShuffle":
            self.up = nn.PixelShuffle(scale)

    def forward(self, x1, x2):
        x2 = self.up(x2)
        shape_x1 = x1.size()
        shape_x2 = x2.size()
        left = 0
        right = 0
        top = 0
        bot = 0
        if shape_x1[3] != shape_x2[3]:
            lef_right = shape_x1[3] - shape_x2[3]
            if lef_right%2 == 0.0:
                left = int(lef_right/2)
                right = int(lef_right/2)
            else:
                left = int(lef_right / 2)
                right = int(lef_right - left)

        if shape_x1[2] != shape_x2[2]:
            top_bot = shape_x1[2] - shape_x2[2]
            if top_bot%2 == 0.0:
                top = int(top_bot/2)
                bot = int(top_bot/2)
            else:
                top = int(top_bot / 2)
                bot = int(top_bot - top)

        reflection_padding = [left, right, top, bot]
        reflection_pad = nn.ReflectionPad2d(reflection_padding)
        x2 = reflection_pad(x2)
        return x2

class CrissCrossAttention(nn.Module):
    """ Criss-Cross Attention Module"""
    def __init__(self, in_dim):
        super(CrissCrossAttention,self).__init__()
        self.query_conv = nn.Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
        self.key_conv = nn.Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1)
        self.value_conv = nn.Conv2d(in_channels=in_dim, out_channels=in_dim, kernel_size=1)
        self.softmax = Softmax(dim=3)
        self.gamma = nn.Parameter(torch.zeros(1))

    def forward(self, x):
        m_batchsize, _, height, width = x.size()
        proj_query = self.query_conv(x)
        proj_query_H = proj_query.permute(0,3,1,2).contiguous().view(m_batchsize*width,-1,height).permute(0, 2, 1)
        proj_query_W = proj_query.permute(0,2,1,3).contiguous().view(m_batchsize*height,-1,width).permute(0, 2, 1)
        proj_key = self.key_conv(x)
        proj_key_H = proj_key.permute(0,3,1,2).contiguous().view(m_batchsize*width,-1,height)
        proj_key_W = proj_key.permute(0,2,1,3).contiguous().view(m_batchsize*height,-1,width)
        proj_value = self.value_conv(x)
        proj_value_H = proj_value.permute(0,3,1,2).contiguous().view(m_batchsize*width,-1,height)
        proj_value_W = proj_value.permute(0,2,1,3).contiguous().view(m_batchsize*height,-1,width)
        energy_H = torch.bmm(proj_query_H, proj_key_H).view(m_batchsize,width,height,height).permute(0,2,1,3)
        energy_W = torch.bmm(proj_query_W, proj_key_W).view(m_batchsize,height,width,width)
        concate = self.softmax(torch.cat([energy_H, energy_W], 3))

        att_H = concate[:,:,:,0:height].permute(0,2,1,3).contiguous().view(m_batchsize*width,height,height)
        #print(concate)
        #print(att_H)
        att_W = concate[:,:,:,height:height+width].contiguous().view(m_batchsize*height,width,width)
        out_H = torch.bmm(proj_value_H, att_H.permute(0, 2, 1)).view(m_batchsize,width,-1,height).permute(0,2,3,1)
        out_W = torch.bmm(proj_value_W, att_W.permute(0, 2, 1)).view(m_batchsize,height,-1,width).permute(0,2,1,3)
        #print(out_H.size(),out_W.size())
        return self.gamma*(out_H + out_W) + x

def _no_grad_trunc_normal_(tensor, mean, std, a, b):
    def norm_cdf(x):
        return (1. + math.erf(x / math.sqrt(2.))) / 2.

    if (mean < a - 2 * std) or (mean > b + 2 * std):
        warnings.warn("mean is more than 2 std from [a, b] in nn.init.trunc_normal_. "
                      "The distribution of values may be incorrect.",
                      stacklevel=2)

    with torch.no_grad():
        # Values are generated by using a truncated uniform distribution and
        # then using the inverse CDF for the normal distribution.
        # Get upper and lower cdf values
        l = norm_cdf((a - mean) / std)
        u = norm_cdf((b - mean) / std)

        # Uniformly fill tensor with values from [l, u], then translate to
        # [2l-1, 2u-1].
        tensor.uniform_(2 * l - 1, 2 * u - 1)

        # Use inverse cdf transform for normal distribution to get truncated
        # standard normal
        tensor.erfinv_()

        # Transform to proper mean, std
        tensor.mul_(std * math.sqrt(2.))
        tensor.add_(mean)

        # Clamp to ensure it's in the proper range
        tensor.clamp_(min=a, max=b)
        return tensor

def trunc_normal_(tensor, mean=0., std=1., a=-2., b=2.):
    """ type: (Tensor, float, float, float, float) -> Tensor """
    """Fills the input Tensor with values drawn from a truncated
    normal distribution. The values are effectively drawn from the
    normal distribution :math:`\mathcal{N}(\text{mean}, \text{std}^2)`
    with values outside :math:`[a, b]` redrawn until they are within
    the bounds. The method used for generating the random values works
    best when :math:`a \leq \text{mean} \leq b`.
    Args:
        tensor: an n-dimensional `torch.Tensor`
        mean: the mean of the normal distribution
        std: the standard deviation of the normal distribution
        a: the minimum cutoff value
        b: the maximum cutoff value
    Examples:
        >>> w = torch.empty(3, 5)
        >>> nn.init.trunc_normal_(w)
    """
    return _no_grad_trunc_normal_(tensor, mean, std, a, b)

def variance_scaling_(tensor, scale=1.0, mode='fan_in', distribution='normal'):
    fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor)
    if mode == 'fan_in':
        denom = fan_in
    elif mode == 'fan_out':
        denom = fan_out
    elif mode == 'fan_avg':
        denom = (fan_in + fan_out) / 2

    variance = scale / denom

    if distribution == "truncated_normal":
        # constant is stddev of standard normal truncated to (-2, 2)
        trunc_normal_(tensor, std=math.sqrt(variance) / .87962566103423978)
    elif distribution == "normal":
        tensor.normal_(std=math.sqrt(variance))
    elif distribution == "uniform":
        bound = math.sqrt(3 * variance)
        tensor.uniform_(-bound, bound)
    else:
        raise ValueError(f"invalid distribution {distribution}")

def lecun_normal_(tensor):
    variance_scaling_(tensor, mode='fan_in', distribution='truncated_normal')

class PatchEmbed(nn.Module):
    """ 2D Image to Patch Embedding
    """
    def __init__(self, img_size=224, kernel_size=16,  stride=16, padding=0, in_chans=3, embed_dim=768):
        super().__init__()
        kernel_size = to_2tuple(kernel_size)
        stride = to_2tuple(stride)
        padding = to_2tuple(padding)

        self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=kernel_size, stride=stride, padding=padding )
        self.norm = nn.BatchNorm2d(embed_dim)

    def forward(self, x):
        # B, C, H, W = x.shape
        x = self.proj(x)
        x = self.norm(x)
        x = x.permute(0,2,3,1)
        return x

class FirstPatchEmbed(nn.Module):
    """ 2D Image to Patch Embedding
    """
    def __init__(self, kernel_size=3,  stride=2, padding=1, in_chans=3, embed_dim=768):
        super().__init__()

        self.proj1 = nn.Conv2d(in_chans, embed_dim//2, kernel_size=kernel_size, stride=stride, padding=padding )
        self.norm1 = nn.BatchNorm2d(embed_dim // 2)
        self.gelu1 = nn.GELU()
        self.proj2 = nn.Conv2d(embed_dim//2, embed_dim, kernel_size=kernel_size, stride=stride, padding=padding )
        self.norm2 = nn.BatchNorm2d(embed_dim)

    def forward(self, x):
        # B, C, H, W = x.shape
        x = self.proj1(x)
        x = self.norm1(x)
        x = self.gelu1(x)
        x = self.proj2(x)
        x = self.norm2(x)
        x = x.permute(0,2,3,1)
        return x

class HighMixer(nn.Module):
    def __init__(self, dim, kernel_size=3, stride=1, padding=1,
        **kwargs, ):
        super().__init__()

        self.cnn_in = cnn_in = dim // 2
        self.pool_in = pool_in = dim // 2

        self.cnn_dim = cnn_dim = cnn_in * 2
        self.pool_dim = pool_dim = pool_in * 2

        self.conv1 = nn.Conv2d(cnn_in, cnn_dim, kernel_size=1, stride=1, padding=0, bias=False)
        self.proj1 = nn.Conv2d(cnn_dim, cnn_dim, kernel_size=kernel_size, stride=stride, padding=padding, bias=False, groups=cnn_dim)
        self.mid_gelu1 = nn.GELU()

        self.Maxpool = nn.MaxPool2d(kernel_size, stride=stride, padding=padding)
        self.proj2 = nn.Conv2d(pool_in, pool_dim, kernel_size=1, stride=1, padding=0)
        self.mid_gelu2 = nn.GELU()

    def forward(self, x):
        # B, C H, W

        cx = x[:,:self.cnn_in,:,:].contiguous()
        cx = self.conv1(cx)
        cx = self.proj1(cx)
        cx = self.mid_gelu1(cx)

        px = x[:,self.cnn_in:,:,:].contiguous()
        px = self.Maxpool(px)
        px = self.proj2(px)
        px = self.mid_gelu2(px)

        hx = torch.cat((cx, px), dim=1)
        return hx

class LowMixer(nn.Module):
    def __init__(self, dim, num_heads=8, qkv_bias=False, attn_drop=0., pool_size=2,
        **kwargs, ):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = head_dim = dim // num_heads
        self.scale = head_dim ** -0.5
        self.dim = dim
        self.pool_size=pool_size
        self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
        self.attn_drop = nn.Dropout(attn_drop)

        self.pool = nn.AvgPool2d(pool_size, stride=pool_size, padding=0, count_include_pad=False) if pool_size > 1 else nn.Identity()
        self.uppool = Upsample_eval(type="Upsample", scale=2) if pool_size > 1 else nn.Identity()
        self.attn = CrissCrossAttention(dim)

    def forward(self, x):
        # B, C, H, W
        B, _, _, _ = x.shape
        xa = self.pool(x)
        _, _, H, W = xa.shape
        xa = self.attn(xa)
        if self.pool_size >1:
            xa = self.uppool(x, xa)
        else:
            xa = self.uppool(xa)
        return xa

class FE(nn.Module):
    def __init__(self, dim, num_heads=8, qkv_bias=False, attn_drop=0., proj_drop=0., attention_heads=1, pool_size=2,
        **kwargs, ):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = head_dim = dim // num_heads

        self.low_dim = low_dim = attention_heads * head_dim
        self.high_dim = high_dim = dim - low_dim

        self.high_mixer = HighMixer(high_dim)
        self.low_mixer = LowMixer(low_dim, num_heads=attention_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, pool_size=pool_size,)

    def forward(self, x):
        B, H, W, C = x.shape
        x = x.permute(0, 3, 1, 2)

        hx = x[:,:self.high_dim,:,:].contiguous()
        hx = self.high_mixer(hx)

        lx = x[:,self.high_dim:,:,:].contiguous()
        lx = self.low_mixer(lx)
        return [hx, lx]

class Mixer(nn.Module):
    def __init__(self, dim, num_heads=8, qkv_bias=False, attn_drop=0., proj_drop=0., attention_heads=1, pool_size=2,
        **kwargs, ):
        super().__init__()
        self.num_heads = num_heads
        self.head_dim = head_dim = dim // num_heads

        self.low_dim = low_dim = attention_heads * head_dim
        self.high_dim = high_dim = dim - low_dim

        self.conv_fuse = nn.Conv2d(low_dim+high_dim*2, low_dim+high_dim*2, kernel_size=3, stride=1, padding=1, bias=False, groups=low_dim+high_dim*2)
        self.proj = nn.Conv2d(low_dim+high_dim*2, dim, kernel_size=1, stride=1, padding=0)
        self.proj_drop = nn.Dropout(proj_drop)

    def forward(self, x):
        x = torch.cat((x[0], x[1]), dim=1)
        x = x + self.conv_fuse(x)
        x = self.proj(x)
        x = self.proj_drop(x)
        x = x.permute(0, 2, 3, 1).contiguous()
        return x

class BaseLayer(nn.Module):

    def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, drop=0., attn_drop=0.,
                 drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, attention_heads=1, pool_size=2,
                 use_layer_scale=False, layer_scale_init_value=1e-5):
        super().__init__()

        self.norm1_1 = norm_layer(dim)
        # self.norm1_2 = norm_layer(dim)

        self.FE1 = FE(dim,  num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)
        self.attn1 = Mixer(dim, num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)
        # self.FE2 = FE(dim,  num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)
        # self.attn2 = Mixer(dim, num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)

        # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()

        self.norm2_1 = norm_layer(dim)
        # self.norm2_2 = norm_layer(dim)

        mlp_hidden_dim = int(dim * mlp_ratio)

        self.mlp1 = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
        # self.mlp2 = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)

        self.use_layer_scale = use_layer_scale
        if self.use_layer_scale:
            # print('use layer scale init value {}'.format(layer_scale_init_value))
            self.layer_scale_1 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)
            self.layer_scale_2 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)

    def forward(self, x):
        if self.use_layer_scale:
            x = x + self.drop_path(self.layer_scale_1 *self.attn1(self.FE1(self.norm1_1(x))))
            x = x + self.drop_path(self.layer_scale_2 * self.mlp1(self.norm2_1(x)))
            # x[1] = x[1] + self.drop_path(self.layer_scale_1 *self.attn2(self.FE2(self.norm1_2(x[1]))))
            # x[1] = x[1] + self.drop_path(self.layer_scale_2 * self.mlp2(self.norm2_2(x[1])))
        else:
            x = x + self.drop_path(self.attn1(self.FE1(self.norm1_1(x))))
            x = x + self.drop_path(self.mlp1(self.norm2_1(x)))
            # x[1] = x[1] + self.drop_path(self.attn1(self.FE1(self.norm1_2(x[1]))))
            # x[1] = x[1] + self.drop_path(self.mlp1(self.norm2_2(x[1])))
        return x

class CrossLayer(nn.Module):

    def __init__(self, dim, num_heads=1, mlp_ratio=4., qkv_bias=False, drop=0., attn_drop=0.,
                 drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, attention_heads=1, pool_size=2,
                 use_layer_scale=False, layer_scale_init_value=1e-5):
        super().__init__()

        self.norm1_1 = norm_layer(dim)
        self.norm1_2 = norm_layer(dim)

        self.FE1 = FE(dim,  num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)
        self.attn1 = Mixer(dim, num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)
        self.FE2 = FE(dim,  num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)
        self.attn2 = Mixer(dim, num_heads=num_heads, qkv_bias=qkv_bias, attn_drop=attn_drop, attention_heads=attention_heads, pool_size=pool_size)

        # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here
        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()

        self.norm2_1 = norm_layer(dim)
        self.norm2_2 = norm_layer(dim)

        mlp_hidden_dim = int(dim * mlp_ratio)

        self.mlp1 = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)
        self.mlp2 = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)

        self.use_layer_scale = use_layer_scale
        if self.use_layer_scale:
            # print('use layer scale init value {}'.format(layer_scale_init_value))
            self.layer_scale_1 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)
            self.layer_scale_2 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)

    def forward(self, x):
        if self.use_layer_scale:
            hx1, lx1 = self.FE1(self.norm1_1(x[0]))
            hx2, lx2 = self.FE2(self.norm2_1(x[1]))

            x[0] = x[0] + self.drop_path(self.layer_scale_1 * self.attn1([hx2, lx1]))
            x[1] = x[1] + self.drop_path(self.layer_scale_1 * self.attn1([hx1, lx2]))

            x[0] = x[0] + self.drop_path(self.layer_scale_2 * self.mlp1(self.norm2_1(x[0])))
            x[1] = x[1] + self.drop_path(self.layer_scale_2 * self.mlp2(self.norm2_2(x[1])))
        else:
            hx1, lx1 = self.FE1(self.norm1_1(x[0]))
            hx2, lx2 = self.FE2(self.norm2_1(x[1]))

            x[0] = x[0] + self.drop_path(self.attn1([hx2, lx1]))
            x[1] = x[1] + self.drop_path(self.attn1([hx1, lx2]))

            x[0] = x[0] + self.drop_path(self.mlp1(self.norm2_1(x[0])))
            x[1] = x[1] + self.drop_path(self.mlp2(self.norm2_2(x[1])))

        return x

class BaseBlock(nn.Module):
    def __init__(self, dim=None, depths=None,
                 num_heads=None, mlp_ratio=4., qkv_bias=True,
                 drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=None,
                 act_layer=None, attention_heads=None, pool_size=2,
                 use_layer_scale=False, layer_scale_init_value=1e-5,
                 **kwargs,
                 ):
        super().__init__()
        self.FE_A = BaseLayer(dim=dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=drop_path_rate, norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads,
                    pool_size=pool_size, use_layer_scale=use_layer_scale, layer_scale_init_value=layer_scale_init_value)
        self.FE_B = BaseLayer(dim=dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=drop_path_rate, norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads,
                    pool_size=pool_size, use_layer_scale=use_layer_scale, layer_scale_init_value=layer_scale_init_value)
    def forward(self, x):
        x[0] = self.FE_A(x[0])
        x[1] = self.FE_B(x[1])
        return x

class CrossBlock(nn.Module):
    def __init__(self, dim=None, depths=None,
                 num_heads=None, mlp_ratio=4., qkv_bias=True,
                 drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=None,
                 act_layer=None, attention_heads=None, pool_size=2,
                 use_layer_scale=False, layer_scale_init_value=1e-5,
                 **kwargs,
                 ):
        super().__init__()
        self.FE_A = BaseLayer(dim=dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=drop_path_rate, norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads,
                    pool_size=pool_size, use_layer_scale=use_layer_scale, layer_scale_init_value=layer_scale_init_value)
        self.FE_B = BaseLayer(dim=dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=drop_path_rate, norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads,
                    pool_size=pool_size, use_layer_scale=use_layer_scale, layer_scale_init_value=layer_scale_init_value)
        self.Fusion = CrossLayer(dim=dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=drop_path_rate, norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads,
                    pool_size=pool_size, use_layer_scale=use_layer_scale, layer_scale_init_value=layer_scale_init_value)
    def forward(self, x):
        x[0] = self.FE_A(x[0])
        x[1] = self.FE_B(x[1])
        x = self.Fusion(x)
        return x

class InceptionTransformerFuse(nn.Module):
    def __init__(self, img_size=224, in_chans=1, out_chans=1, embed_dims=None, depths=None,
                 num_heads=None, mlp_ratio=4., qkv_bias=True,
                 drop_rate=0., attn_drop_rate=0., drop_path_rate=0., embed_layer=PatchEmbed, norm_layer=None,
                 act_layer=None, weight_init='', image_range=1,
                 attention_heads=None, use_layer_scale=False, layer_scale_init_value=1e-5,
                 checkpoint_path=None,
                 **kwargs,
                 ):

        super().__init__()
        st2_idx = sum(depths[:1])
        st3_idx = sum(depths[:2])
        depth = sum(depths)

        norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6)
        act_layer = act_layer or nn.GELU

        dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)]  # stochastic depth decay rule

        self.mean = torch.zeros([1,1,1,1])
        self.img_range = image_range
        self.num_patches = img_size

        # feature extractrion
        self.patch_embed1_1 = FirstPatchEmbed(kernel_size=3, stride=1, padding=1,in_chans=in_chans, embed_dim=embed_dims[0])
        self.patch_embed1_2 = FirstPatchEmbed(kernel_size=3, stride=1, padding=1,in_chans=in_chans, embed_dim=embed_dims[0])
        num_patches = img_size
        self.pos_embed1_1 = nn.Parameter(torch.zeros(1, num_patches, num_patches, embed_dims[0]))
        self.pos_embed1_2 = nn.Parameter(torch.zeros(1, num_patches, num_patches, embed_dims[0]))
        self.blocks1 = nn.Sequential(
            *[BaseBlock(
                    dim=embed_dims[0], num_heads=num_heads[0], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads[i],
                    use_layer_scale = use_layer_scale, layer_scale_init_value=layer_scale_init_value,
                    pool_size=2)
            for i in range(st2_idx)])

        self.patch_embed2_1 = embed_layer(kernel_size=3, stride=1, padding=1, in_chans=embed_dims[0], embed_dim=embed_dims[1])
        self.patch_embed2_2 = embed_layer(kernel_size=3, stride=1, padding=1, in_chans=embed_dims[0], embed_dim=embed_dims[1])

        # Fusion
        self.pos_embed2_1 = nn.Parameter(torch.zeros(1, num_patches, num_patches, embed_dims[1]))
        self.pos_embed2_2 = nn.Parameter(torch.zeros(1, num_patches, num_patches, embed_dims[1]))
        self.blocks2 = nn.Sequential(
            *[CrossBlock(
                    dim=embed_dims[1], num_heads=num_heads[1], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=drop_path_rate, norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads[i],
                    pool_size=2)
              for i in range(st2_idx,st3_idx)])

        self.patch_embed3 = embed_layer(kernel_size=3, stride=1, padding=1, in_chans=embed_dims[1]*2, embed_dim=embed_dims[2])

        # Reconstructer
        self.pos_embed3 = nn.Parameter(torch.zeros(1, num_patches, num_patches, embed_dims[2]))
        self.blocks3 = nn.Sequential(
            *[BaseLayer(
                    dim=embed_dims[2], num_heads=num_heads[2], mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop=drop_rate,
                    attn_drop=attn_drop_rate, drop_path=drop_path_rate, norm_layer=norm_layer, act_layer=act_layer, attention_heads=attention_heads[i],
                    pool_size=2)
              for i in range(st3_idx, depth)])

        self.conv1 = nn.Conv2d(in_channels=embed_dims[2], out_channels=embed_dims[2]//2, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(in_channels=embed_dims[2]//2, out_channels=out_chans, kernel_size=3, stride=1, padding=1)

        self.init_weights(weight_init)

    def init_weights(self, mode=''):
        trunc_normal_(self.pos_embed1_1, std=.02)
        trunc_normal_(self.pos_embed2_1, std=.02)
        trunc_normal_(self.pos_embed1_2, std=.02)
        trunc_normal_(self.pos_embed2_2, std=.02)
        trunc_normal_(self.pos_embed3, std=.02)

        self.apply(_init_vit_weights)

    def _init_weights(self, m):
        # this fn left here for compat with downstream users
        _init_vit_weights(m)

    @torch.jit.ignore
    def no_weight_decay(self):
        return {'pos_embed', 'cls_token', 'dist_token'}

    def get_classifier(self):
        if self.dist_token is None:
            return self.head
        else:
            return self.head, self.head_dist

    def _get_pos_embed(self, pos_embed, num_patches_def, H, W):
        if H * W == num_patches_def * num_patches_def:
            return pos_embed
        else:
            return F.interpolate(
                pos_embed.permute(0, 3, 1, 2),
                size=(H, W), mode="bilinear").permute(0, 2, 3, 1)

    def forward(self, x1, x2):
        # Feature Extraction
        x1 = self.patch_embed1_1(x1)
        x2 = self.patch_embed1_2(x2)
        B, H, W, C = x1.shape
        x1 = x1 + self._get_pos_embed(self.pos_embed1_1, self.num_patches, H, W)
        x2 = x2 + self._get_pos_embed(self.pos_embed1_2, self.num_patches, H, W)
        [x1, x2] = self.blocks1([x1, x2])

        # Fusion
        x1 = x1.permute(0, 3, 1, 2)
        x2 = x2.permute(0, 3, 1, 2)
        x1 = self.patch_embed2_1(x1)
        x2 = self.patch_embed2_2(x2)
        B, H, W, C = x1.shape
        x1 = x1 + self._get_pos_embed(self.pos_embed2_1, self.num_patches, H, W)
        x2 = x2 + self._get_pos_embed(self.pos_embed2_2, self.num_patches, H, W)
        [x1, x2] = self.blocks2([x1, x2])

        x1 = x1.permute(0, 3, 1, 2)
        x2 = x2.permute(0, 3, 1, 2)

        x = torch.cat((x1, x2),dim=1)
        x = self.patch_embed3(x)
        B, H, W, C = x.shape
        x = x + self._get_pos_embed(self.pos_embed3, self.num_patches, H, W)
        x = self.blocks3(x)

        x = x.permute(0, 3, 1, 2)
        x = self.conv1(x)
        x = self.conv2(x)

        return x

def _init_vit_weights(module: nn.Module, name: str = '', head_bias: float = 0.):
    """ ViT weight initialization
    * When called without n, head_bias, jax_impl args it will behave exactly the same
      as my original init for compatibility with prev hparam / downstream use cases (ie DeiT).
    """
    if isinstance(module, nn.Linear):
        if name.startswith('head'):
            nn.init.zeros_(module.weight)
            nn.init.constant_(module.bias, head_bias)
        elif name.startswith('pre_logits'):
            lecun_normal_(module.weight)
            nn.init.zeros_(module.bias)
        else:
            trunc_normal_(module.weight,  std=.02)
            if module.bias is not None:
                nn.init.zeros_(module.bias)
    elif isinstance(module, (nn.LayerNorm, nn.GroupNorm, nn.BatchNorm2d)):
        nn.init.zeros_(module.bias)
        nn.init.ones_(module.weight)
    elif isinstance(module, nn.Conv2d):
        trunc_normal_(module.weight, std=.02)
        if module.bias is not None:
            nn.init.constant_(module.bias, 0)

# test = torch.randn(1,1,64,64)

# depths = [3, 3, 3]
# embed_dims = [ 96, 96, 96]
# num_heads = [6, 6, 6]
# attention_heads = [1]*3+[3]*3+[1]*3
# model = InceptionTransformerFuse(img_size=224,
#         depths=depths,
#         embed_dims=embed_dims,
#         num_heads=num_heads,
#         attention_heads=attention_heads,
#         use_layer_scale=True, layer_scale_init_value=1e-6)

# print(model)
# _, _, h_old, w_old = test.size()
# left = 0
# right = 0
# top = 0
# bot = 0
# if w_old//32*32 != w_old:
#     lef_right = (w_old//32 + 1)*32 - w_old
#     if lef_right%2 == 0.0:
#         left = int(lef_right/2)
#         right = int(lef_right/2)
#     else:
#         left = int(lef_right / 2)
#         right = int(lef_right - left)
# if h_old//32*32 != h_old:
#     top_bot = (h_old//32 + 1)*32 - h_old
#     if top_bot%2 == 0.0:
#         top = int(top_bot/2)
#         bot = int(top_bot/2)
#     else:
#         top = int(top_bot / 2)
#         bot = int(top_bot - top)

# reflection_padding = [left, right, top, bot]
# reflection_pad = torch.nn.ReflectionPad2d(reflection_padding)
# test = reflection_pad(test)
# a = model.forward(test, test)
# a = a[:,:,top:top+h_old,left:left+w_old]
# print(a.shape)


import os

import pygame

class MainMenu:
    def __init__(self):
        pygame.init()

        self.winWidth = 1000
        self.winHeight = 600
        self.screen = pygame.display.set_mode((self.winWidth, self.winHeight))

        self.pushed = None
        self.y = 100

        self.motionButton = None

        self.directory = os.getcwd()

        pygame.mouse.set_visible(False)

        self.set_interface()
        surf = pygame.image.load('sprites/ForGUI/cursor1.png')

        running = True
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.pushed = self.buttons[-1][1]
                    running = False
                if event.type == pygame.MOUSEBUTTONUP:
                    for i in range(len(self.buttons)):
                        if self.buttons[i][1].collidepoint(event.pos):
                            self.pushed = self.buttons[i][1]
                            running = False
                if event.type == pygame.MOUSEMOTION:
                    for i in range(len(self.buttons)):
                        if self.buttons[i][1].collidepoint(event.pos) and \
                                self.buttons[i][1] != self.motionButton:
                            self.motionButton = self.buttons[i][1]
                            self.i = i
                            self.status = 0
                    if not (self.motionButton is None):
                        if not self.motionButton.collidepoint(event.pos):
                            self.motionButton = None
            self.render()

            rect = surf.get_rect(topleft=pygame.mouse.get_pos())
            self.screen.blit(surf, rect)

            pygame.display.flip()

    def render(self):
        self.screen.blit(self.background_surf, self.background_rect)

        for button in self.buttons:
            if self.motionButton == button[1]:
                self.screen.blit(self.images[self.i][self.status], button[1])
                self.status += 1

                if self.status == 30:
                    self.status = 0
            else:
                self.screen.blit(button[0], button[1])

    def set_interface(self):
        directory = os.getcwd()

        # LOAD BACKGROUND
        self.background_surf = pygame.image.load(
            directory + '/backgrounds/quizFone.png')
        self.background_surf = pygame.transform.scale(self.background_surf,
                                                      (1000, 600))
        self.background_rect = self.background_surf.get_rect(
            bottomright=(1000, 600))

        self.continue_btn = load_image(
            'sprites/ForGUI/Blue buttons/Play blue button 300x80.png')
        self.continue_btn_Rect = self.continue_btn.get_rect(
            bottomright=(355, 205))

        self.options_btn = load_image(
            'sprites/ForGUI/Blue buttons/more blue button 300x80.png')
        self.options_btn_Rect = self.options_btn.get_rect(
            bottomright=(355, 305))

        self.quit_btn = load_image(
            'sprites/ForGUI/Blue buttons/Quit blue button 300x80.png')
        self.quit_btn_Rect = self.quit_btn.get_rect(bottomright=(355, 405))

        self.buttons = [(self.continue_btn, self.continue_btn_Rect),
                        (self.options_btn, self.options_btn_Rect),
                        (self.quit_btn, self.quit_btn_Rect)]

        self.images = [[]]
        for i in range(1, 31):
            self.images[-1].append(load_image(
                self.directory + '/sprites/ForGUI/Blue buttons/play blue button 300x80 hover/' +
                'play button blue 300x80 (' + str(i) + ').png'))

        self.images.append([])
        for i in range(1, 31):
            self.images[-1].append(load_image(
                self.directory + '/sprites/ForGUI\Blue buttons\more blue button 300x80 hover/' +
                'more blue button 300x80 (' + str(i) + ').png'))

        self.images.append([])
        for i in range(1, 31):
            self.images[-1].append(load_image(
                self.directory + '/sprites/ForGUI\Blue buttons\Quit blue button 300x80 hover/' +
                'quit blue button 300x80 (' + str(i) + ').png'))

def load_image(name):
    image = pygame.image.load(name)
    image = pygame.transform.scale(image, (300, 80))

    return image

if __name__ == "__main__":
    MainMenu()


#! /usr/bin/env python

"""Creats the IR sky flats.
Authors
-------
    Heather Kurtz, December 8, 2017
Use
-------

"""

import glob
import logging
from multiprocessing import Pool
import os
import sys

from astropy.convolution import Gaussian2DKernel
from astropy.io import fits
from astropy.stats import gaussian_fwhm_to_sigma
from astropy.convolution import convolve
from astropy.stats import sigma_clip

#from mx import DateTime
import numpy as np
from photutils import DAOStarFinder
from photutils import detect_sources
from photutils import detect_threshold
#import Statistics
#import Stats2
import tempfile
import threading
from threading import Thread
import scipy
from scipy.stats import sigmaclip
from scipy.ndimage import gaussian_filter
from scipy import ndimage
import matplotlib.pyplot as plt

from pyql.database.ql_database_interface import session
from pyql.database.ql_database_interface import Master
from pyql.database.ql_database_interface import IR_flt_0

def quary_ql(proid,filt):
#I need to use the ql quary to get: directories for images,

    results = session.query(Master.dir, Master.rootname).\
          join(IR_flt_0).\
          filter(
              #IR_flt_0.targname == 'tungsten',
              IR_flt_0.filter == filt,
              IR_flt_0.detector == 'ir',
              #IR_flt_0.imagetyp == 'flat',
              IR_flt_0.exptime > 300,
              IR_flt_0.proposid == proid)
# Turn the roots and dirs into locations we can use later.
    locales = ['{}_flt.fits'.format(os.path.join(item.dir, item.rootname)) for item in results]

    return locales

def get_data(file):

    hdulist=fits.open(file)
    data=hdulist[1].data
    dq=hdulist[3].data
    hdulist.close()
    return( data, dq)

def dq_mask(dq,data,ims):
    bit_mask = (4+16+32+128)#+512)
    dq0 = np.bitwise_and(dq,np.zeros(np.shape(dq),'Int16')+ bit_mask)
    dq0==0
    dq0[dq0>0]=1
    data[dq!=0]=np.nan
    ims[dq0>0]=1
    data[ims>0]=np.nan

def find_sources(data):

    threshold = detect_threshold(data, snr=3.)
    sigma = 2.0 * gaussian_fwhm_to_sigma    # FWHM = 2.
    kernel = Gaussian2DKernel(sigma, x_size=3, y_size=3)
    kernel.normalize()
    print(kernel)
    segm = detect_sources(data, threshold, npixels=5, filter_kernel=kernel)
    return(segm)

#def persistince_masks():

def mask_sources(seg):
	seg[seg>0.003]=1
	seg[seg<0.003]=0
	return(seg)

def convolv_data(seg_arr):
	g = Gaussian2DKernel(stddev=15)
	# Convolve data
	dataC = convolve(seg_arr, g, boundary='extend')
	return(dataC)

def wrtie_file(file,plo,pro):
  spro=str(pro)
  file_name=spro+file[-18:-8]+'mdi.fits'
  fits.writeto(file_name, plo,overwrite=True)

def normalize(data):
  values = data[~np.isnan(data)]
  values, clow, chigh = sigmaclip(values, low=3, high=3)
  mean = np.mean(values)
  image=data/mean
  return(image)

def Stack(data_array_1):
  image_median = np.nanmedian(data_array_1, axis=0)
  image_mean= np.nanmean(data_array_1, axis=0)
  image_sum= np.nansum(data_array_1, axis=0)
  image_std= np.nanstd(data_array_1, axis=0)

  return(image_mean,image_median,image_sum,image_std)

def sigclip(data):
  #sigmaclip the data
  clip_data = sigma_clip(data, sigma=2, iters=3)
  return(clip_data)

def data_size(data):
  nans = np.isnan(data)
  no_nans_data = data[~nans]
  nan_size=no_nans_data.size
  datsize=data.size
  return(nan_size,datsize)

def main():
	#quarry ql put values into dictionary
	#put into forloop for filters

	#for i in len(dic[rootnames]):
	#	direct=dic[dir(i)]

		#open file

		#test file to see it good to use

	#	if quality =='good':
			#do everything else (find source, mask source normalize, and save)
	#	if qality=='bad':
	#		continue

	#test data:

  #list_file=glob.glob('/grp/hst/wfc3a/GO_Links/12167/Visit05/*flt.fits')
  pro_list=['12167']
  #pro_list=['11108','11142','11149','11153','11166','11189','11202','11208','11343','11359',
   #         '11519','11520','11534','11541','11557','11563','11584','11597','11600','11644',
    #        '11650','11666','11669','11694','11700','11702','11735','11838','11840','11587']
  list_files=[]
  for i in range(len(pro_list)):
    list_file=quary_ql(pro_list[i],'F160W')
    for j in range(len(list_file)):
      list_files.append(list_file[j])
  #list_file=quary_ql('12025','F160W')
  list_files=['/grp/hst/wfc3a/GO_Links/12167/Visit02/ibhg02q2q_flt.fits','/grp/hst/wfc3a/GO_Links/12167/Visit10/ibhg10b6q_flt.fits']
  hdr = fits.getheader(list_files[0], 1)
  nx = hdr['NAXIS1']
  ny = hdr['NAXIS2']
  nf = len(list_files)
  set_data=fits.getdata(list_files[0], 1)
  data_array = np.empty((nf, ny, nx), dtype=float)
  for i , f in enumerate(list_files):
    print(f)
    hdr1 = fits.getheader(f, 0)
    propid=hdr1['PROPOSID']
    data,dq=get_data(f)
    segm=find_sources(data)
    #print(segm.cmap)
    fig, (ax1) = plt.subplots(1, 1, figsize=(5, 5))
    ax1.imshow(segm, origin='lower')#,cmap=segm.cmap(random_state=12345))
    plt.show()
    seg=segm.array
    seg[seg>0]=1.0
    dataC=convolv_data(seg)
    im=mask_sources(dataC)
    fig, (ax1) = plt.subplots(1, 1, figsize=(5, 5))
    ax1.imshow(im, origin='lower')
    plt.show()
    dq_mask(dq,data,im)
    image=normalize(data)
    fig, (ax1) = plt.subplots(1, 1, figsize=(5, 5))
    ax1.imshow(image, origin='lower')
    plt.show()
    clipdata=sigclip(image)
    fig, (ax1) = plt.subplots(1, 1, figsize=(5, 5))
    ax1.imshow(clipdata, origin='lower')
    plt.show()
    nan_siz,dat_siz=data_size(clipdata)
    wrtie_file(f,image,propid)
    if nan_siz>(dat_siz*0.8):
        continue
    else:
        wrtie_file(f,image,propid)
        data_array[i, :, :] = clipdata
  #S_mean,S_median,S_sum,S_std=Stack(data_array)

  #fits.writeto('test_F160_10_mean_sn5.fits', S_mean,overwrite=True)
  #fits.writeto('test_F160_10_median_sn5.fits', S_median,overwrite=True)

main()

	#read in masked images for stacking
	#combine all images


from flask import Flask, render_template, url_for, redirect, flash
from flask_wtf import FlaskForm
from app.forms import AddVisitForm, AddInterviewForm, AddInitialInterviewForm, AddFollowupInterviewForm
from app import app

from flaskext.mysql import MySQL

from config import Config
import os

import datetime
"""
this creates connection to the MySQL Database
"""
mysql = MySQL()
mysql.init_app(app)
app.config['MYSQL_DATABASE_USER'] = 'obaid'
app.config['MYSQL_DATABASE_PASSWORD'] = 'aaa123123123'
app.config['MYSQL_DATABASE_DB'] = 'projectDB'
app.config['MYSQL_DATABASE_HOST'] = 'localhost'

"""
Routes to the initial page at /index or / route

Renders index.html page

It passes buttons data to the index.html template
"""
@app.route('/')
@app.route('/index')
def index():
    buttons = {
        'add_visit':'Add Visit',
        'add_interview':'Interview',
        'lookup_visit':'Lookup Visit',
        'other':'Other',
        }
    return render_template('index.html', buttons = buttons)

"""
Routes to the add visit page at /add_visit route
Renders add_visit.html page

Add Visit button from index.html page also gets you to this page

This page has a form and uses the AddVisitForm() class to pass
the form fields to add_visit.html template

if the form is valid when the user submits, then a connection
is opened and a cursor is created. Then the form values are
passed to the sql statement which is executed and committed.

user data is also queried and passed to the html template
"""
@app.route('/add_visit', methods=['GET', 'POST'])
def add_visit():
    buttons = {
        'add':'Add Visit'
    }
    form = AddVisitForm()
    if form.validate_on_submit():
        conn = mysql.connect()
        cursor = conn.cursor()

        visitId = form.visit_ID.data
        thc = form.thc_number.data
        # date = form.date.data
        visitNum = form.visit_num.data

        sql = '''INSERT INTO Visit (THC, Visit_Num)
        VALUES ({},{})'''.format(thc,visitNum)

        cursor.execute(sql)
        conn.commit()

        sql = '''SELECT * FROM Visit
                NATURAL JOIN Patient
                ORDER BY Visit_ID DESC LIMIT 1'''
        cursor.execute(sql)
        x = None
        for row in cursor.fetchall():
            x = row

        data = {
            'data': "Visit ID: {}, Date: {}, THC#: {}, Patient Name: {} {}, Visit Number: {}".format(x[1], x[3], x[0], x[4], x[5], x[2])
        }

        cursor.close()
        conn.close()
        # return 'Submitted values: \n date: {}'.format(date)
        return render_template('add_interview.html',
        form = AddInterviewForm(),
        buttons = {'initial_followup':'Initial/Followup',
        'thi':'THI',
        'tfi':'TFI',
        'add_md':'Add MD'},
        data = data)

    return render_template('add_visit.html', butons = buttons, form = form)

"""
Routes to /add_interview page
Renders add_interview.html

This page has a form and uses the AddVisitForm() class to pass
the form fields to add_visit.html template

Displays information for the most recent patient at the top
"""
@app.route('/add_interview')
def add_interview():
    buttons = {
        'initial_followup':'Initial/Followup',
        'thi':'THI',
        'tfi':'TFI',
        'add_md':'Add MD'
    }
    form = AddInterviewForm()

    conn = mysql.connect()
    cursor = conn.cursor()

    sql = '''SELECT * FROM Visit
            NATURAL JOIN Patient
            ORDER BY Visit_ID DESC LIMIT 1'''
    cursor.execute(sql)

    x = None
    for row in cursor.fetchall():
        x = row

    data = {
        'data': "Visit ID: {}, Date: {}, THC#: {}, Patient Name: {} {}, Visit Number: {}".format(x[1], x[2], x[0], x[4], x[5], x[3])
    }

    cursor.close()
    conn.close()

    if form.validate_on_submit():
        return "submitted"

    return render_template('add_interview.html', form = form, buttons = buttons, data = data)

"""
Routes to /init_intervew page
Renders init_interview.html

This page has a form and uses the AddInitialInterviewForm() class to pass
the form fields to init_interview.html template

Displays information for the most recent patient at the top

Inserts Values from form : clinicNum, thcNum

redirects to /followup_interview if form submits

"""
@app.route('/init_interview', methods = ['GET', 'POST'])
def init_interview():
    buttons = {
        'save':'Save'
    }
    form = AddInitialInterviewForm()

    conn = mysql.connect()
    cursor = conn.cursor()
    sql = '''SELECT * FROM Visit
            NATURAL JOIN Patient
            ORDER BY Visit_ID DESC LIMIT 1'''
    cursor.execute(sql)

    x = None
    for row in cursor.fetchall():
        x = row

    data = {
        'data': "Visit ID: {}, Date: {}, THC#: {}, Patient Name: {} {}, Visit Number: {}".format(x[1], x[3], x[0], x[4], x[5], x[2])
    }
    cursor.close()
    conn.close()

    if form.validate_on_submit():
        conn = mysql.connect()
        cursor = conn.cursor()

        clinicNum = form.clinic_number.data
        thcNum = form.thc.data

        sql = '''INSERT INTO Interview (Clinic_Num,
        THC_Num) VALUE ({},{})'''.format(clinicNum,thcNum)
        cursor.execute(sql)
        conn.commit()
        cursor.close()
        conn.close()

        # conn = mysql.connect()
        # cursor = conn.cursor()

        # firstname = form.first_name.data
        # lastname = form.last_name.data

        # sql = '''INSERT INTO Patient(THC, First_Name, Last_Name, DOB, SSN, Insurance, Tel, Email)

        return redirect('/followup_interview')

    return render_template('init_interview.html', form = form, buttons = buttons, data = data)

"""
Routes to /followup_interview page
Renders followup_interview.html

This page has a form and uses the AddFollowupInterviewForm() class to pass
the form fields to init_interview.html template

Inserts Values from form : clinicNum, thcNum

redirects to /index if form submits

"""

@app.route('/followup_interview' , methods = ['GET', 'POST'])
def followup_interview():
    buttons = {
        'save':'Save'
    }
    form = AddFollowupInterviewForm()
    # if form.validate_on_submit():
    #     return redirect('/index')
    # if request.method == "POST":
    #     return redirect('/index')
    if form.validate_on_submit():
        conn = mysql.connect()
        cursor = conn.cursor()

        clinicNum = form.clinic_number.data
        thcNum = form.thc.data

        sql = '''INSERT INTO Interview (Clinic_Num,
        THC_Num) VALUE ({},{})'''.format(clinicNum,thcNum)
        cursor.execute(sql)
        conn.commit()
        cursor.close()
        conn.close()

        return redirect('/index')

    return render_template('followup_interview.html', form = form, buttons = buttons)

"""
Routes to /look_up page
Renders look_up.html

Queries all patient names and returns them
"""
@app.route('/lookup_visit')
def lookup_visit():
    conn = mysql.connect()
    cursor = conn.cursor()
    sql = '''SELECT First_Name FROM Visit
            NATURAL JOIN Patient
            WHERE Visit_Num = 1
            ORDER BY THC DESC LIMIT 10 '''
    cursor.execute(sql)
    x = []
    for row in cursor.fetchall():
        x.append(row)
    data = {
        'data':x
    }
    return render_template('lookup_visit.html', data = data)


import copy, math
import pickle as pkl

import time

import config

import dgl
import numpy as np
import pandas as pd

import torch as th
import torch.nn as nn

from easydict import EasyDict
from models.frgcn import FRGCN
from models.fgat import FGAT
from models.flgcn import FLGCN

from prettytable import PrettyTable

from sklearn.metrics import accuracy_score, f1_score, roc_auc_score
from data_generator_task1 import get_task1_dataloader
from torch import optim

from utils import get_args_from_yaml, get_logger

def count_parameters(model):
    table = PrettyTable(["Modules", "Parameters"])
    total_params = 0
    for name, parameter in model.named_parameters():
        if not parameter.requires_grad:
            continue
        params = parameter.numel()
        table.add_row([name, params])
        total_params += params
    print(table)
    print(f"Total Trainable Params: {total_params}")
    return total_params

# TODO Task 2 evaluation method --> top 100
def get_rank(iids, answer_iid):
    if answer_iid not in set(iids):
        return 101
    else:
        rank = iids.index(answer_iid) +1
        return rank

task2_valid_query_df = pd.read_csv('./processed_data/itemset_item_valid_query.csv')
task2_valid_answer_df = task2_valid_query_df.query('answer==1')

def evaluate_task2(model, loader, device):
    # Evaluate AUC, ACC
    model.eval()
    val_labels = []
    val_preds = []
    for batch in loader:
        with th.no_grad():
            preds = model(batch[0].to(device))
        # labels = batch[1].to(device)
        # val_labels.extend(labels.cpu().tolist())
        val_preds.extend(preds.cpu().tolist())

    itemset_item_answer_dict={k:v for k,v in zip(task2_valid_answer_df.itemset_id,task2_valid_answer_df.item_id)}

    preds_df = pd.DataFrame({
        'itemset_id': task2_valid_query_df.itemset_id,
        'item_id': task2_valid_query_df.item_id,
        'score': val_preds,
    })
    accs = []
    ranks = []
    for itemset_id, sub_df in preds_df.groupby('itemset_id'):
        sub_df = sub_df.sort_values('score', ascending=False)
        result = sub_df.item_id[:100]
        answer_iid = itemset_item_answer_dict[itemset_id]
        rank = get_rank(list(result), answer_iid)
        ranks.append(rank)
        if rank == 101:
            accs.append(0)
        else:
            accs.append(1)
    val_rank = np.mean(ranks)
    val_acc = np.mean(accs)
    return None, val_rank, val_acc

def evaluate(model, loader, device):
    # Evaluate AUC, ACC
    model.eval()
    val_labels = []
    val_preds = []
    for batch in loader:
        with th.no_grad():
            preds = model(batch[0].to(device))
        labels = batch[1].to(device)
        val_labels.extend(labels.cpu().tolist())
        val_preds.extend(preds.cpu().tolist())

    val_auc = roc_auc_score(val_labels, val_preds)
    val_acc = accuracy_score(list(map(round, val_labels)), list(map(round, val_preds)))
    # val_f1 = f1_score(list(map(round,val_labels)), list(map(round,val_preds)))
    return val_auc, None, val_acc

def train_epoch(model, optimizer, loader, device, logger, log_interval):
    model.train()

    epoch_loss = 0.0
    iter_loss = 0.0
    iter_mse = 0.0
    iter_cnt = 0
    iter_dur = []
    mse_loss_fn = nn.MSELoss().to(device)
    # bce_loss_fn = nn.BCELoss().to(device)

    for iter_idx, batch in enumerate(loader, start=1):
        t_start = time.time()

        inputs = batch[0].to(device)
        labels = batch[1].to(device)
        preds = model(inputs)
        # print(preds[:8], labels[:8])

        optimizer.zero_grad()
        loss = mse_loss_fn(preds, labels)
        loss.backward()
        optimizer.step()

        epoch_loss += loss.item() * preds.shape[0]
        iter_loss += loss.item() * preds.shape[0]
        iter_mse += ((preds - labels) ** 2).sum().item()
        iter_cnt += preds.shape[0]
        iter_dur.append(time.time() - t_start)

        if iter_idx % log_interval == 0:
            logger.debug(
                f"Iter={iter_idx}, loss={iter_loss/iter_cnt:.4f}, rmse={math.sqrt(iter_mse/iter_cnt):.4f}, time={np.average(iter_dur):.4f}"
            )
            iter_loss = 0.0
            iter_mse = 0.0
            iter_cnt = 0
            if iter_idx == 500:
                break

    return epoch_loss / len(loader.dataset)

def train(args: EasyDict, train_loader, test_loader, logger):
    th.manual_seed(0)
    np.random.seed(0)
    # dgl.random.seed(0)

    ### prepare data and set model
    in_feats = config.IN_FEATS
    if args.model_type == "FRGCN":
        model = FRGCN(
            in_feats=in_feats,
            latent_dim=args.latent_dims,
            num_relations=args.num_relations,
            num_bases=4,
            regression=True,
            edge_dropout=args.edge_dropout,
        ).to(args.device)

    if args.model_type == "FGAT":
        model = FGAT(
            in_nfeats=in_feats,
            in_efeats=args.num_relations,
            latent_dim=args.latent_dims,
            edge_dropout=args.edge_dropout,
        ).to(args.device)

    if args.model_type == "FLGCN":
        model = FLGCN(
            in_feats=in_feats,
            latent_dim=args.latent_dims,
            edge_dropout=args.edge_dropout,
        ).to(args.device)

    if args.parameters is not None:
        model.load_state_dict(th.load(f"./parameters/{args.parameters}"))

    optimizer = optim.Adam(
        model.parameters(), lr=args.train_lr, weight_decay=args.weight_decay
    )
    logger.info("Loading network finished ...\n")

    count_parameters(model)

    best_epoch = 0
    best_auc, best_rank, best_acc = 0, 1000, 0

    logger.info(f"Start training ... learning rate : {args.train_lr}")
    epochs = list(range(1, args.train_epochs + 1))

    eval_func_map = {
        "task1": evaluate,
        "task2": evaluate_task2,
    }
    eval_func = eval_func_map.get(args.dataset, evaluate)
    for epoch_idx in epochs:
        logger.debug(f"Epoch : {epoch_idx}")

        train_loss = train_epoch(
            model,
            optimizer,
            train_loader,
            args.device,
            logger,
            log_interval=args.log_interval,
        )
        # train_loss = 0
        test_auc, test_rank, test_acc = eval_func(model, test_loader, args.device)
        if test_auc is None:
            test_auc = -1
        if test_rank is None:
            test_rank = -1
        eval_info = {
            "epoch": epoch_idx,
            "train_loss": train_loss,
            "test_auc": test_auc,
            "test_rank": test_rank,
            "test_acc": test_acc,
        }
        logger.info(
            "=== Epoch {}, train loss {:.4f}, test auc {:.4f}, test rank {:.4f}, test acc {:.4f} ===".format(
                *eval_info.values()
            )
        )

        if epoch_idx % args.lr_decay_step == 0:
            for param in optimizer.param_groups:
                param["lr"] = args.lr_decay_factor * param["lr"]
            print("lr : ", param["lr"])

        if test_rank == -1 :
            if best_auc < test_auc:
                logger.info(f'new best test auc {test_auc:.4f} acc {test_acc:.4f} ===')
                best_epoch = epoch_idx
                best_auc = test_auc
                best_acc = test_acc
                best_lr = args.train_lr
                best_state = copy.deepcopy(model.state_dict())
        else:
            if best_rank > test_rank:
                logger.info(f'new best test rank {test_rank:.4f} acc {test_acc:.4f} ===')
                best_epoch = epoch_idx
                best_rank = test_rank
                best_acc = test_acc
                best_lr = args.train_lr
                best_state = copy.deepcopy(model.state_dict())

    th.save(best_state, f'./parameters/{args.key}_{args.dataset}_{best_auc:.4f}.pt')
    logger.info(f"Training ends. The best testing auc {best_auc:.4f} acc {best_acc:.4f} at epoch {best_epoch}")
    return best_auc, best_acc, best_lr

import yaml
from data_generator_task1 import get_task1_dataloader
from data_generator_task2 import get_task2_dataloader

DATALOADER_MAP = {
    "task1": get_task1_dataloader,
    "task2": get_task2_dataloader,
}

def main():
    with open("./train_configs/train_list.yaml") as f:
        files = yaml.load(f, Loader=yaml.FullLoader)
    file_list = files["files"]
    for f in file_list:
        args = get_args_from_yaml(f)
        logger = get_logger(name=args.key, path=f"{args.log_dir}/{args.key}.log")
        logger.info("train args")
        for k, v in args.items():
            logger.info(f"{k}: {v}")

        best_lr = None
        sub_args = args
        best_auc_acc_lr_list = []

        dataloader_manager = DATALOADER_MAP.get(sub_args.dataset)

        train_loader, valid_loader, _ = dataloader_manager(
            data_path=sub_args.dataset,
            batch_size=sub_args.batch_size,
            num_workers=config.NUM_WORKER,
        )

        for lr in args.train_lrs:
            sub_args["train_lr"] = lr
            best_auc_acc_lr = train(sub_args, train_loader, valid_loader, logger=logger)
            best_auc_acc_lr_list.append(best_auc_acc_lr)

        best_auc, best_acc, best_lr = max(best_auc_acc_lr_list, key = lambda x: x[0])
        best_auc_list = [x[0] for x in best_auc_acc_lr_list]
        best_acc_list = [x[1] for x in best_auc_acc_lr_list]
        if sub_args.dataset == 'task2':
            logger.info(f"**********The final best testing RANK {best_auc:.4f} ACC {best_acc:.4f} at lr {best_lr}********")
            logger.info(f"**********The mean testing RANK {np.mean(best_auc_list):.4f}, {np.std(best_auc_list):.4f} ********")
        else:
            logger.info(f"**********The final best testing AUC {best_auc:.4f} ACC {best_acc:.4f} at lr {best_lr}********")
            logger.info(f"**********The mean testing AUC {np.mean(best_auc_list):.4f}, {np.std(best_auc_list):.4f} ********")
        logger.info(f"**********The mean testing ACC {np.mean(best_acc_list):.4f}, {np.std(best_acc_list):.4f} ********")

if __name__ == "__main__":
    main()


#!/usr/bin/python3
"""
module: test_amenity
resources: unittest module, Amenity class,
TestAmenity class
"""
import unittest
from models.amenity import Amenity

class TestAmenity(unittest.TestCase):
    def setUp(self):
        self.amenity_1 = Amenity()

    def test_name(self):
        self.assertEqual(Amenity.name, "")
        self.assertEqual(self.amenity_1.name, "")
        self.amenity_1.name = "WiFi"
        self.assertEqual(self.amenity_1.name, "WiFi")
        self.assertEqual(Amenity.name, "")

if __name__ == "__main__":
    unittest.main()


import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Activation
from keras.layers import Conv2D, MaxPooling2D
from keras.utils.vis_utils import plot_model
from keras.utils import np_utils
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('../data')

X_train, Y_train = mnist.train.images, mnist.train.labels
X_test, Y_test = mnist.test.images, mnist.test.labels

Y_train = np_utils.to_categorical(Y_train,num_classes=10)
Y_test = np_utils.to_categorical(Y_test,num_classes=10)

# Build MLP
model = Sequential()

model.add(Dense(units=256,
		input_dim=784,
		kernel_initializer='normal',
		activation='relu'))

model.add(Dense(units=10,
		kernel_initializer='normal',
		activation='softmax'))

model.summary()

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X_train, Y_train, batch_size=500, epochs=10, verbose=1, validation_data=(X_test, Y_test))
model.save('mlp_fashion_mnist.h5')

loss, accuracy = model.evaluate(X_test, Y_test, verbose=0)
print('loss:', loss)
print('accuracy:', accuracy)


import sys
import time
import argparse
import os
import logging
import glob

def parse_args():
    """ this class includes training options """

    # basic parameters
    parser = argparse.ArgumentParser(description='weather4cast')

    # load data from file
    parser.add_argument('--data_path', type=str, default='/home/yaoyi/lin00786/weather4cast/preprocess-data/',
                        help='data path, dynamic variables')
    parser.add_argument('--sample_path', type=str, default='./samples.csv',
                        help='data path, splitting information')
    parser.add_argument('--region_id', type=str, default='R1',
                        help="region_id to load data from. Default: R1")

    parser.add_argument('--model_name', type=str, default='test', help='model name')
    parser.add_argument('--result_path', type=str, default='./results',
                         help='result path, including log file and model file')

    parser.add_argument('--source_var_idx', type=str, default='0', help='Default: temperature')
    parser.add_argument('--target_var_idx', type=str, default='0', help='Default: temperature')

    # training parameters
    parser.add_argument('--gpu_id', type=int, default=0, help='GPU id')
    parser.add_argument('--num_epochs', type=int, default=600, help='number of epochs for training')
    parser.add_argument('--batch_size', type=int, default=32, help='training batch size')
    parser.add_argument('--lr', type=float, default=0.001, help='initial learning rate for adam')
    parser.add_argument('--weight_decay', type=float, default=0.001, help='the scalar for l2 loss')
    parser.add_argument('--patience', type=int, default=10, help='the patience for early stop')
    parser.add_argument('--log_interval', type=int, default=1, help='the interval to test model')
    parser.add_argument('--h_dim', type=int, default=64, help='size of hidden')
    parser.add_argument('--kernel_size', type=int, default=3, help='size of kernel')

    # model parameters
    parser.add_argument('--seq_len', type=int, default=4, help='input sequence length')
    parser.add_argument('--horizon', type=int, default=1, help='output sequence length')
    parser.add_argument('--use_static', action='store_true', help='use static features')

    # others
    parser.add_argument('--verbose', action='store_false', help='print more debugging information. Default: True')
    parser.add_argument('--num_test', type=int, default=None, help='the number of testing samples during inference')

    args = parser.parse_args()
    return args

def verbose(params):

    if params['verbose']:
        logging.basicConfig(filename=params['log_file_path'],
                            level=logging.INFO,
                            format='%(asctime)s - %(message)s',
                            datefmt='%y-%m-%d %H:%M')
        logging.getLogger().addHandler(logging.StreamHandler())

        for k, v in params.items():
            logging.info(f'{k} - {v}')

def input_check(params):

    if not os.path.exists(params['data_path']):
        print('Data path {} does not exist.'.format(params['data_path']))
        sys.exit(-1)

    if not os.path.exists(params['sample_path']):
        print('Sample path {} does not exist.'.format(params['sample_path']))
        sys.exit(-1)

    if params['mode'] == 'train':
        if not os.path.exists(params['result_path']):
            os.makedirs(params['result_path'])
            os.makedirs(params['model_path'])
            os.makedirs(params['log_path'])

    elif params['mode'] == 'test':
        if not os.path.exists(params['model_path']):
            print('Model path {} does not exist.'.format(params['model_path']))
            sys.exit(-1)

        model_path = '{}/*.ckpt'.format(params['model_path'])
        model_path = glob.glob(model_path)
        assert len(model_path)==1, f'All these files were found: {model_path}'
        params['model_path'] = model_path[0]

    return params

def load_param_dict(args=None, mode='train'):

    param_dict = dict()
    param_dict['mode'] = mode
    param_dict['vars'] = ['temperature', 'ctth_tempe', 'ishai_skt', 'crr_intensity', 'asii_turb_trop_prob', 'cma']

    if args is None:
        param_dict['data_path'] = '/home/yaoyi/lin00786/weather4cast/preprocess-data/'
        param_dict['sample_path'] = './samples.csv'
        param_dict['region_id'] = 'R1'
        param_dict['model_name'] = 'Seq2Seq_seq4_hoz1_in1_out1_kernel1'
        param_dict['result_path'] = '/home/yaoyi/lin00786/weather4cast/weather4cast-test-lightning/results'
        param_dict['source_var_idx'] = [1]
        param_dict['target_var_idx'] = [1]

        param_dict['gpu_id'] = 0
        param_dict['num_epochs'] = 100
        param_dict['batch_size'] = 32
        param_dict['lr'] = 0.001
        param_dict['weight_decay'] = 0.001
        param_dict['patience'] = 10
        param_dict['log_interval'] = 1
        param_dict['h_dim'] = 32
        param_dict['kernel_size'] = 3

        param_dict['seq_len'] = 4
        param_dict['horizon'] = 1
        param_dict['use_static'] = False

        param_dict['num_test'] = 1
        param_dict['verbose'] = True

    else:
        param_dict['data_path'] = args.data_path
        param_dict['sample_path'] = args.sample_path
        param_dict['region_id'] = args.region_id
        param_dict['model_name'] = args.model_name
        param_dict['result_path'] = args.result_path
        param_dict['source_var_idx'] = [int(i) for i in list(args.source_var_idx)]
        param_dict['target_var_idx'] = [int(i) for i in list(args.target_var_idx)]

        param_dict['gpu_id'] = args.gpu_id
        param_dict['num_epochs'] = args.num_epochs
        param_dict['batch_size'] = args.batch_size
        param_dict['lr'] = args.lr
        param_dict['weight_decay'] = args.weight_decay
        param_dict['patience'] = args.patience
        param_dict['log_interval'] = args.log_interval
        param_dict['h_dim'] = args.h_dim
        param_dict['kernel_size'] = args.kernel_size

        param_dict['seq_len'] = args.seq_len
        param_dict['horizon'] = args.horizon
        param_dict['use_static'] = args.use_static

        param_dict['num_test'] = args.num_test
        param_dict['verbose'] = args.verbose

    param_dict['source_vars'] = [param_dict['vars'][i] for i in param_dict['source_var_idx']]
    param_dict['target_vars'] = [param_dict['vars'][i] for i in param_dict['target_var_idx']]

    if mode == 'train':
        model_name = ''
        if param_dict['model_name'] == 'seq2seq':
            model_name = '{}_seq{}_hoz{}_in{}_out{}_kernel{}_hdim{}'.format(param_dict['model_name'],
                                                                            param_dict['seq_len'],
                                                                            param_dict['horizon'],
                                                                            ''.join([str(i) for i in param_dict['source_var_idx']]),
                                                                            ''.join([str(i) for i in param_dict['target_var_idx']]),
                                                                            param_dict['kernel_size'],
                                                                            param_dict['h_dim'],)
        if param_dict['model_name'] == 'unet':
            model_name = '{}_seq{}_hoz{}_in{}_out{}'.format(param_dict['model_name'],
                                                            param_dict['seq_len'],
                                                            param_dict['horizon'],
                                                            ''.join([str(i) for i in param_dict['source_var_idx']]),
                                                            ''.join([str(i) for i in param_dict['target_var_idx']]),)

        if param_dict['model_name'] == 'convttrans':
            model_name = '{}_seq{}_hoz{}_in{}_out{}_kernel{}_hdim{}'.format(param_dict['model_name'],
                                                                            param_dict['seq_len'],
                                                                            param_dict['horizon'],
                                                                            ''.join([str(i) for i in param_dict['source_var_idx']]),
                                                                            ''.join([str(i) for i in param_dict['target_var_idx']]),
                                                                            param_dict['kernel_size'],
                                                                            param_dict['h_dim'],)

        param_dict['result_path'] = os.path.join(param_dict['result_path'],
                                                 model_name + '_' + str(int(time.time())))
    else:
        model_name = args.model_name
        param_dict['result_path'] = os.path.join(param_dict['result_path'], model_name)

    param_dict['model_path'] = os.path.join(param_dict['result_path'], 'models')
    param_dict['log_path'] = os.path.join(param_dict['result_path'], 'logs')
    param_dict['log_file_path'] = os.path.join(param_dict['log_path'], model_name + f'_{mode}.log')

    print(param_dict.keys())
    param_dict = input_check(param_dict)
    verbose(param_dict)
    return param_dict


from tkinter import *
import tkinter.font as font
import dbConnection
import datetime
from functools import partial
import time
import os
import ledBlinks
from mfrc522 import *
import signal

# Function that checks the correct tag
def rfid_scan():
    labelTitle["text"] = "You need to show your tag first!"
    labelTitle["fg"] = "Red"
    labelTitle.update()

    labelParagraph["text"] = "Please place your tag to the top of the reader..."
    #labelParagraph["fg"] = "Red"
    labelParagraph.update()

    continue_reading = True

    # Capture SIGINT for cleanup when the script is aborted
    def end_read(signal,frame):
        global continue_reading
        print ("Ctrl+C captured, ending read.")
        continue_reading = False

    # Hook the SIGINT
    signal.signal(signal.SIGINT, end_read)

    # Create an object of the class MFRC522
    MIFAREReader = MFRC522()

    # Welcome message
    print ("Welcome to the MFRC522 data read example")
    print ("Press Ctrl-C to stop.")

    # This loop keeps checking for chips. If one is near it will get the UID and authenticate
    while continue_reading:
        # Scan for cards
        (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)

        # If a card is found
        if status == MIFAREReader.MI_OK:
            print ("Card detected")

        # Get the UID of the card
        (status,uid) = MIFAREReader.MFRC522_Anticoll()

        # If we have the UID, continue
        if status == MIFAREReader.MI_OK:

            # Print UID
            print ("Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3])+','+str(uid[4]))
            # This is the default key for authentication
            key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]

            # Select the scanned tag
            MIFAREReader.MFRC522_SelectTag(uid)

            #ENTER Your Card UID here
            my_uid = [199,176,169,199,25]

            #Check to see if card UID read matches your card UID
            if uid == my_uid:                #Open the Doggy Door if matching UIDs
                buttonStart.destroy()
                labelTitle["text"] = "Access granted!"
                labelTitle["fg"] = "Green"
                labelTitle.update()

                labelParagraph["text"] = "Loading up the application..."
                labelParagraph.update()

                continue_reading = False
                window.after(3000, orderWindow)
            else:                            #Don't open if UIDs don't match
                buttonStart.destroy()
                labelTitle["text"] = "Tag is not authorized!"
                labelTitle.update()

                labelParagraph["text"] = "Returning to welcome screen..."
                labelParagraph.update()
                continue_reading = False
                window.after(3000, lambda: main(True))

def main(isNewOrder=False):

    if (isNewOrder == False):
        global window
        window = Tk()

        window.geometry("800x480")

        window.attributes("-fullscreen", True)

        # Defining the count (this will be used as a countdown timer when the customer has completed an order and is at the end screen)
        global count
        count = 10
    else:
        # Reset the count back to 10
        count = 10
        for child in window.winfo_children():
            child.destroy()

    # Defining fonts
    global primaryFont
    primaryFont = font.Font(family="Nunito", size=25, weight="bold")
    global secondaryFont
    secondaryFont = font.Font(family="Nunito", size=15, weight="normal")
    global thirdFont
    thirdFont = font.Font(family="Nunito Black", size=15, weight="normal")

    # Defining the background
    bg = PhotoImage(
        file=f"{os.path.dirname(os.path.abspath(__file__))}/blackBgLogo.png")

    global label1
    label1 = Label(window, image=bg, borderwidth=0)
    label1.place(x=0, y=0)

    global labelTitle
    labelTitle = Label(window, text="Welcome!", bg="black",
                       fg="white", font=primaryFont)
    labelTitle.pack(pady=10)

    global labelParagraph
    labelParagraph = Label(window, text="Start a new order below:",
                           bg="black", fg="white", font=secondaryFont)
    labelParagraph.pack(pady=5)

    global totalOrders
    totalOrders = 0

    global buttonFrame
    buttonFrame = Frame(window, bg="black")

    global buttonCart
    buttonCart = Button(
        buttonFrame, text=f"Order amount: {totalOrders}", bg="black", fg="white", font=secondaryFont)
    global buttonReset
    buttonReset = Button(buttonFrame, text="Reset", bg="black",
                         fg="white", font=secondaryFont)

    global buttonStart
    buttonStart = Button(window, text="Start", bg="black",
                         fg="white", font=primaryFont, width=25, command=rfid_scan)
    buttonStart.pack(pady=100)

    window.mainloop()

# When the customer wants to reset his purchase, do this:
def resetOrder(cocktailName, cocktailAmountsBase, cocktailDictOfZeroes, cocktailAmounts):
    global totalOrders

    # Reset the cocktailDictOfZeroes key values to 0:
    for cocktail in cocktailDictOfZeroes:
        cocktailDictOfZeroes[cocktailName]["amount"] = 0

    # Reset the totalOrders to 0:
    totalOrders = 0

    # Unpack buttoncart and buttonreset from the UI:
    buttonCart.pack_forget()
    buttonReset.pack_forget()

    # Reset the corresponding cocktail's value to the original base value:
    for cocktail in cocktailAmounts:
        print("cocktail", cocktail)
        cocktailAmounts[cocktail] = cocktailAmountsBase[cocktail]

    # Change the paragraph text back to original:
    labelParagraph["text"] = "No refunds!"

# When the customer clicks any of the buttons to add an order to cart, do this:
def orderAdded(cocktailName, cocktailAmounts, cocktailDictOfZeroes, cocktailAmountsBase):
    global totalOrders
    global buttonCart
    global buttonReset
    global buttonFrame

    # Checking if the current cocktail (that the customer added to the cart) has supplies left.
    # If not then don't minus 1 from the cocktailAmounts dictionary.
    if (cocktailAmounts[cocktailName] != -1):
        cocktailAmounts[cocktailName] -= 1
        if (cocktailAmounts[cocktailName] != -1):
            totalOrders += 1
            cocktailDictOfZeroes[cocktailName]["amount"] += 1

    print("cocktaildictof:", cocktailDictOfZeroes)
    # Below is the shopping validators.
    # User can't, for example, add a product to cart if it's sold out.
    if (cocktailAmounts[cocktailName] >= 0 and totalOrders == 1):
        buttonFrame.pack()
        buttonCart["text"] = f"Order ({totalOrders} drink)"
        buttonCart.pack(padx=10, pady=10, side=LEFT, fill=BOTH)
        buttonReset.pack(padx=10, pady=10, side=LEFT, fill=BOTH)
        print("orders", totalOrders)
    elif (totalOrders > 1 and cocktailAmounts[cocktailName] >= 0):
        buttonCart["text"] = f"Order ({totalOrders} drinks)"
        print("orders", totalOrders)
    elif (cocktailAmounts[cocktailName] == -1 and totalOrders > 1):
        print(cocktailAmounts)
        print(cocktailDictOfZeroes)
        buttonCart["text"] = f"Order ({totalOrders} drinks)"
        labelParagraph["text"] = f"{cocktailName} is all bought out!"
        print(f"{cocktailName} is all bought out!")
    elif (cocktailAmounts[cocktailName] == -1 and totalOrders == 1):
        buttonCart["text"] = f"Order ({totalOrders} drink)"
        labelParagraph["text"] = f"{cocktailName} is all bought out!"
        print(f"{cocktailName} is all bought out!")
    elif (cocktailAmounts[cocktailName] == -1 and totalOrders == 0):
        labelParagraph["text"] = f"{cocktailName} is all bought out!"
        print(f"{cocktailName} is all bought out!")

    # Use winfo_children method to get the corresponding cocktail button (the button that the user clicks to add an order to the cart)
    # We then minus the cocktail from the corresponding cocktail button when the user adds that cocktail to the cart (or in other words, we change the buttons text)
    for child in window.winfo_children():
        for childChild in child.winfo_children():
            for childChildChild in childChild.winfo_children():
                for childChildChildChild in childChildChild.winfo_children():
                    if (type(childChildChildChild) == Button):
                        if (childChildChildChild["text"] == f"{cocktailName} \n left: {cocktailAmounts[cocktailName] + 1} drinks"):
                            childChildChildChild["text"] = f"{cocktailName} \n left: {cocktailAmounts[cocktailName]} drinks"
                            print("Fourth: ", childChildChildChild["text"])

    # Use partial so that tkinter doesn't immediately run the functions that we put in the command value
    transactionDoneArg = partial(transactionDone, cocktailDictOfZeroes)
    resetOrderArg = partial(resetOrder, cocktailName,
                            cocktailAmountsBase, cocktailDictOfZeroes, cocktailAmounts)

    buttonCart["command"] = transactionDoneArg
    buttonReset["command"] = resetOrderArg

# When user the user orders the cocktails, run this:
def orderCompleted(cocktailsBought=0):
    # Delete the frame
    for child in window.winfo_children():
        if (type(child) == Frame):
            child.destroy()

    # Change the label title to let the user know their purchase has gone through
    labelTitle["text"] = "Thank you for your purchase!"

    global count

    # Start the led simulation if count is 10 (in other words at the beginning of this function)
    if (count == 10):
        window.after(100, lambda: ledBlinks.doLedBlinks(cocktailsBought))

    if (count >= 0):
        # If count is 10 and the led simulation is still going on do this:
        if (count == 10):

            # Make a simple estimated waiting time for the customer
            estimatedWaitingTime = 0
            for cocktail in cocktailsBought:
                numberOfBlinks = cocktailsBought[cocktail]["redBlinks"] + \
                    cocktailsBought[cocktail]["greenBlinks"] + \
                    cocktailsBought[cocktail]["blueBlinks"]
                estimatedWaitingTime += numberOfBlinks * \
                    cocktailsBought[cocktail]["amount"]

            labelParagraph[
                "text"] = f"Please wait for your drink(s) to fill up completely... \n \n \n \n \n \n Estimated waiting time: {estimatedWaitingTime} seconds"
            count -= 1

        # After the led simulation is done then change the label paragraph as follows:
        else:
            labelParagraph[
                "text"] = f"All done! \n Remember to drink responsibly :) \n \n \n \n \n \n Returning to the welcome screen in:\n{count}"
            count -= 1

        # After 1 second start this function again (to imitate a countdown timer)
        window.after(1000, orderCompleted)

    # If count is 0 go back to the main function and start over
    else:
        main(True)

def transactionDone(cocktailDictOfZeroes):
    time = datetime.datetime.now()

    day = time.strftime("%d")
    month = time.strftime("%m")
    year = time.strftime("%Y")
    time = time.strftime("%X")

    transactionTime = f"{day}/{month}/{year} {time}"

    # Making a new cocktail dictionary that will have only the cocktails, which the customer bought (amount is greater than 0)
    cocktailsBought = {}

    for cocktailName in cocktailDictOfZeroes:
        if (cocktailDictOfZeroes[cocktailName]["amount"] > 0):
            newBody = {"cocktail": cocktailName,
                       "amount": cocktailDictOfZeroes[cocktailName]["amount"], "pricePerDrink": cocktailDictOfZeroes[cocktailName]["pricePerDrink"], "date": transactionTime}
            newBodyWithBlinks = {"cocktail": cocktailName, "amount": cocktailDictOfZeroes[cocktailName]["amount"],
                                 "redBlinks": cocktailDictOfZeroes[cocktailName]["redBlinks"], "greenBlinks": cocktailDictOfZeroes[cocktailName]["greenBlinks"], "blueBlinks": cocktailDictOfZeroes[cocktailName]["blueBlinks"]}
            cocktailsBought[cocktailName] = newBodyWithBlinks
            dbConnection.transaction(newBody)

    orderCompleted(cocktailsBought)

def orderWindow():
    labelTitle["text"] = "Add cocktails to the cart below:"
    labelTitle["fg"] = "White"
    labelParagraph["text"] = "No refunds!"

    frame = Frame(window, bg="black")

    canvas = Canvas(frame, height=300, bg="black")
    frame2 = Frame(canvas, bg="black")

    scrollbar = Scrollbar(frame, orient="vertical",
                          command=canvas.yview, bg="black")

    canvas.create_window((0, 0), window=frame2, anchor="nw")

    time.sleep(0.5)

    allSupplies = dbConnection.findAllSupplies()

    cocktailAmounts = {}
    cocktailAmountsBase = {}
    cocktailDictOfZeroes = {}
    for cocktail in allSupplies:
        cocktailAmounts[cocktail["cocktail"]] = cocktail["supplyLeft"]
        cocktailAmountsBase[cocktail["cocktail"]] = cocktail["supplyLeft"]
        cocktailDictOfZeroes[cocktail["cocktail"]] = {
            "amount": 0, "pricePerDrink": cocktail["pricePer250ml"], "redBlinks": cocktail["redBlinks"], "greenBlinks": cocktail["greenBlinks"], "blueBlinks": cocktail["blueBlinks"]}

    for supply in allSupplies:
        actionArg = partial(
            orderAdded, supply["cocktail"], cocktailAmounts, cocktailDictOfZeroes, cocktailAmountsBase)
        cocktail = supply["cocktail"]
        cocktail = Button(
            frame2, text=f"{supply['cocktail']} \n Left: {supply['supplyLeft']} drinks \n Price: {supply['pricePer250ml']}€", bg="black", fg="white", font=thirdFont, command=actionArg)
        cocktail.config(highlightthickness=3)
        cocktail.pack(pady=5, fill=BOTH)

    frame2.update()
    canvas.configure(yscrollcommand=scrollbar.set,
                     scrollregion="0 0 0 %s" % frame2.winfo_height())
    canvas.config(highlightthickness=0)

    canvas.pack(side=LEFT)
    scrollbar.pack(side=RIGHT, fill=Y)
    frame.pack()

# run the main function
main()


def current_position(matrix):
    for i in range(len(matrix)):
        for j in range(len(matrix[i])):
            if matrix[i][j] == "Y":
                return i, j

def is_outside(row, column, rows, columns):
    return row < 0 or column < 0 or row >= rows or column >= columns
def move_in_matrix(matrix, current_postion, direction, steps):
    row, col = current_postion
    if direction == "up":
        row -= steps
    elif direction == "down":
        row += steps
    elif direction == "left":
        col -= steps
    elif direction == "right":
        col += steps
    return row, col

rows, cols = [int(i) for i in input().split(", ")]

decorations = 0
gifts = 0
cookies = 0

matrix = [input().split(" ") for j in range(rows)]

# print(*matrix, sep="\n")

while True:
    command = input()
    if command == "End":
        break
    command = command.split("-")

    if len(command) == 5:
        action, row, col, value = command
        row, col, value = int(row), int(col), int(value)
        if row < 0 or row >= rows or col < 0 or col >= cols:
            print("Invalid coordinates")
        else:
            if action == "Add":
                matrix[row][col] = int(matrix[row][col]) + value
            elif action == "Subtract":
                matrix[row][col] = int(matrix[row][col]) - value
    else:
        print("Invalid coordinates")


import os
import pandas as pd
import numpy as np
import random
from datetime import datetime
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

from argparse import ArgumentParser

def load_data(filename):

    try:
        df = pd.read_csv(filename)
        print("Data has",len(df),"rows and", len(df.columns),"columns.")
    except:
        print("Load  " + filename + " data error!")
        exit()
    return df

def save_data(data ,filename):
    data.to_csv(filename)
    print("save data " + filename)

if __name__ == '__main__':

    parser = ArgumentParser()

    parser.add_argument( dest='A', help='input data name [A]')
    parser.add_argument(dest='B', help='output data name [B]')

    if parser.parse_args().A is None:
        dataname = "pollution_us_2000_2016-cleaned.csv"
    else:
        dataname = parser.parse_args().A

    if parser.parse_args().B is None:
        output = "output.csv"
    else:
        output = parser.parse_args().B

    print(dataname, output)

    data = load_data(dataname)
    coor = load_data("coordinates.csv")
    df_export = pd.merge(data, coor, on=['State', 'County', 'City'], how='left')

    print(df_export)

    save_data(df_export, output)


from stirling import stirling
from itertools import chain
import hypothesis.strategies as ST
from hypothesis import given

def test_1_element_1_group():
    elements = [1]
    actual = tuple(stirling(elements, num_sets=1))
    # yapf: disable
    expected = (
        ((1, ), ),
    )
    # yapf: enable
    assert actual == expected

def test_2_elements_1_group():
    elements = [0, 1]
    actual = tuple(stirling(elements, num_sets=1))
    # yapf: disable
    expected = (
        ((0, 1), ),
    )
    # yapf: enable
    assert actual == expected

def test_2_elements_2_group():
    elements = [0, 1]
    actual = tuple(stirling(elements, num_sets=2))
    # yapf: disable
    expected = (
        ((0,), (1,) ),
    )
    # yapf: enable
    assert actual == expected

def test_six_choose_three():
    elements = [1, 2, 3, 4, 5, 6]
    expected_count = 90
    actual = tuple(stirling(elements, num_sets=3))
    actual_count = len(actual)
    assert actual_count == expected_count

@ST.composite
def elements_and_num_partitions(draw, elements=ST.integers()):
    values = draw(
        ST.sets(elements, min_size=1, max_size=10)
    )  # This max_size is important because if it's too large things can take a loooong time
    num_groups = draw(ST.integers(min_value=1, max_value=len(values)))
    return (values, num_groups)

@given(elements_and_num_partitions())
def test_correct_number_of_groups(params):
    elements, num_partitions = params
    for partioning in tuple(stirling(elements, num_partitions)):
        assert len(partioning) == num_partitions

@given(elements_and_num_partitions())
def test_all_elements_present_in_partitioning(params):
    elements, num_partitions = params
    for partitioning in stirling(elements, num_partitions):
        partitioned_elements = tuple(chain(*partitioning))
        assert len(partitioned_elements) == len(elements)
        assert set(partitioned_elements) == elements


from http import HTTPStatus

from django.test import TestCase, Client

from posts.models import Post, Group, User

class PostsURLTests(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.test_author = User.objects.create(
            username='TestAuthor',
        )
        cls.test_group = Group.objects.create(
            title='Тестовое название группы',
            slug='test-group',
            description='Тестовое описание',
        )
        cls.test_post = Post.objects.create(
            title='Тестовый заголовок',
            text='Тестовый текст',
            author=cls.test_author,
            group=cls.test_group,
        )

    def setUp(self):
        self.guest_client = Client()
        self.user = User.objects.create_user(username='TestUser')
        self.authorized_client = Client()
        self.authorized_client.force_login(self.user)
        self.authorized_author = Client()
        self.authorized_author.force_login(PostsURLTests.test_author)

        self.urls_dict_guest_users = {
            '/': 'posts/index.html',
            f'/group/{self.test_group.slug}/': 'posts/group_list.html',
            f'/profile/{self.test_author.username}/': 'posts/profile.html',
            f'/posts/{self.test_post.pk}/': 'posts/post_detail.html',
        }

        self.urls_dict_authorized_users = {
            '/': 'posts/index.html',
            f'/group/{self.test_group.slug}/': 'posts/group_list.html',
            f'/profile/{self.test_author.username}/': 'posts/profile.html',
            f'/posts/{self.test_post.pk}/': 'posts/post_detail.html',
            '/create/': 'posts/create_post.html',
            '/follow/': 'posts/follow.html',
        }

    def test_posts_urls_guest_users(self):
        """Проверка работы ссылок для неавторизованного пользователя."""
        for url, template in self.urls_dict_guest_users.items():
            response = self.guest_client.get(url)
            with self.subTest(url=url):
                self.assertEqual(
                    response.status_code, HTTPStatus.OK
                )
            with self.subTest(url=url):
                self.assertTemplateUsed(response, template)
        response_create = self.guest_client.get('/create/', follow=True)
        response_edit = self.guest_client.get(
            f'/posts/{self.test_post.pk}/edit/', follow=True
        )
        response_follow = self.guest_client.get(
            f'/profile/{self.test_author.username}/follow/', follow=True
        )
        response_unfollow = self.guest_client.get(
            f'/profile/{self.test_author.username}/unfollow/', follow=True
        )
        self.assertRedirects(
            response_create, '/auth/login/?next=/create/'
        )
        self.assertRedirects(
            response_edit,
            f'/auth/login/?next=/posts/{self.test_post.pk}/edit/'
        )
        self.assertRedirects(
            response_follow,
            f'/auth/login/?next=/profile/{self.test_author.username}/follow/'
        )
        self.assertRedirects(
            response_unfollow,
            f'/auth/login/?next=/profile/{self.test_author.username}/unfollow/'
        )

    def test_posts_urls_authorized_users(self):
        """Проверка работы ссылок для авторизованного пользователя."""
        for url, template in self.urls_dict_authorized_users.items():
            response = self.authorized_client.get(url)
            with self.subTest(url=url):
                self.assertEqual(
                    response.status_code, HTTPStatus.OK
                )
            with self.subTest(url=url):
                self.assertTemplateUsed(response, template)
        response_follow = self.authorized_client.get(
            f'/profile/{self.test_author.username}/follow/', follow=True
        )
        self.assertRedirects(
            response_follow,
            f'/profile/{self.test_author.username}/'
        )
        response_unfollow = self.authorized_client.get(
            f'/profile/{self.test_author.username}/unfollow/', follow=True
        )
        self.assertRedirects(
            response_unfollow,
            f'/profile/{self.test_author.username}/'
        )

    def test_posts_urls_author(self):
        """Проверка работы ссылок для авторизованного автора."""
        response = self.authorized_author.get(
            f'/posts/{self.test_post.pk}/edit/'
        )
        self.assertEqual(response.status_code, HTTPStatus.OK)

    def test_unexisting_page(self):
        """Проверка работы несуществующих страниц."""
        response_guest = self.guest_client.get('/unexisting_page/')
        self.assertEqual(response_guest.status_code, HTTPStatus.NOT_FOUND)


odd =  int(input("정수를 입력해 주세요. >"))

#홀수, 짝수
# 1,3,5,7,9
#2,4,6,8,0
# 음수 인덱스를 이용

# print(input)
if odd % 2 == 1 : # 입력 받은 값의 나머지가 1이라면 홀수
    print("홀수입니다.")
else :
    print("짝수입니다.")


"""
Advent of Code 2017

https://adventofcode.com/2017/day/11

--- Day 11: Hex Ed ---

Crossing the bridge, you've barely reached the other side of the stream when
a program comes up to you, clearly in distress. "It's my child process," she
says, "he's gotten lost in an infinite grid!"

Fortunately for her, you have plenty of experience with infinite grids.

Unfortunately for you, it's a hex grid.

The hexagons ("hexes") in this grid are aligned such that adjacent hexes can
be found to the north, northeast, southeast, south, southwest, and northwest:

  \ n  /
nw +--+ ne
  /    \
-+      +-
  \    /
sw +--+ se
  / s  \

You have the path the child process took. Starting where he started, you need
to determine the fewest number of steps required to reach him. (A "step"
means to move from the hex you are in to any adjacent hex.)

For example:

  * ne,ne,ne is 3 steps away.
  * ne,ne,sw,sw is 0 steps away (back where you started).
  * ne,ne,s,s is 2 steps away (se,se).
  * se,sw,se,sw,sw is 3 steps away (s,s,sw).

Your puzzle answer was 698.

--- Part Two ---

How many steps away is the furthest he ever got from his starting position?

Although it hasn't changed, you can still get your puzzle input.

Your puzzle answer was 1435.
"""

def cancel_out_steps(direction, opposite_direction):
    if direction > opposite_direction:
        direction = direction - opposite_direction
        opposite_direction = 0
    elif direction < opposite_direction:
        opposite_direction = opposite_direction - direction
        direction = 0
    else:
        direction = 0
        opposite_direction = 0

    return direction, opposite_direction

def merge_two_steps(direction_1, direction_2, merged_direction):
    if direction_1 > direction_2:
        merged_direction = merged_direction + direction_2
        direction_1 = direction_1 - direction_2
        direction_2 = 0
    elif direction_1 < direction_2:
        merged_direction = merged_direction + direction_1
        direction_2 = direction_2 - direction_1
        direction_1 = 0
    else:
        merged_direction = merged_direction + direction_2
        direction_1 = 0
        direction_2 = 0

    return direction_1, direction_2, merged_direction

def caluclate_distance(path):
    hexes = len(path)

    north_steps = sum([1 for step in path if step == 'n'])
    north_east_steps = sum([1 for step in path if step == 'ne'])
    north_west_steps = sum([1 for step in path if step == 'nw'])
    south_steps = sum([1 for step in path if step == 's'])
    south_east_steps = sum([1 for step in path if step == 'se'])
    south_west_steps = sum([1 for step in path if step == 'sw'])

    total = 0
    new_total = -1
    while not new_total == total:
        total = new_total

        # Replace
        north_steps, south_steps = cancel_out_steps(north_steps, south_steps)
        north_west_steps, south_east_steps = cancel_out_steps(
            north_west_steps, south_east_steps)
        north_east_steps, south_west_steps = cancel_out_steps(
            north_east_steps, south_west_steps)

        # Merge
        north_east_steps, south_steps, south_east_steps = merge_two_steps(
            north_east_steps, south_steps, south_east_steps)
        south_east_steps, north_steps, north_east_steps = merge_two_steps(
            south_east_steps, north_steps, north_east_steps)
        north_west_steps, south_steps, south_west_steps = merge_two_steps(
            north_west_steps, south_steps, south_west_steps)
        south_west_steps, north_steps, north_west_steps = merge_two_steps(
            south_west_steps, north_steps, north_west_steps)
        north_east_steps, north_west_steps, north_steps = merge_two_steps(
            north_east_steps, north_west_steps, north_steps)
        south_east_steps, south_west_steps, south_steps = merge_two_steps(
            south_east_steps, south_west_steps, south_steps)

        new_total = sum([north_steps,
                         north_east_steps,
                         north_west_steps,
                         south_steps,
                         south_east_steps,
                         south_west_steps,])
    return total

def farthest_distance(path):
    return max([caluclate_distance(path[:i+1]) for i in range(len(path))])

def main():
    filename = 'day_11_path.txt'
    with open(filename, 'r') as path_file:
        path = path_file.read().strip().split(',')
    print("Shortest distance:", caluclate_distance(path))
    print("Farthest distance:", farthest_distance(path))

if __name__ == '__main__':
    main()