import torch
import torch.nn as nn
import numpy as np
import time
from torchvision import transforms
import cv2
import os
import sys
sys.path.insert(0,"..")
from prepare_data.utils import *
import torch.nn.functional as F
import datetime
class MtcnnDetector(object):
    def __init__(self,pnet,rnet,onet,min_face_size=20,stride=2,threshold=[0.6, 0.7, 0.7],scale_factor=0.79, slide_window=False,use_gpu=True):
        self.pnet_detector = pnet
        self.rnet_detector = rnet
        self.onet_detector = onet
        self.min_face_size = min_face_size
        self.stride = stride
        self.thresh = threshold
        self.scale_factor = scale_factor
        self.slide_window = slide_window
        self.use_gpu = use_gpu

    def convert_to_square(self, bbox):
        """
            convert bbox to square
        Parameters:
        ----------
            bbox: numpy array , shape n x 5
                input bbox
        Returns:
        -------
            square bbox
        """
        square_bbox = bbox.copy()

        h = bbox[:, 3] - bbox[:, 1] + 1
        w = bbox[:, 2] - bbox[:, 0] + 1
        max_side = np.maximum(h, w)
        square_bbox[:, 0] = bbox[:, 0] + w * 0.5 - max_side * 0.5
        square_bbox[:, 1] = bbox[:, 1] + h * 0.5 - max_side * 0.5
        square_bbox[:, 2] = square_bbox[:, 0] + max_side - 1
        square_bbox[:, 3] = square_bbox[:, 1] + max_side - 1
        return square_bbox

    def calibrate_box(self, bbox, reg):
        """
            calibrate bboxes
        Parameters:
        ----------
            bbox: numpy array, shape n x 5 传入rnet网络的图片box 在原图中的坐标 相当于nx1,ny1,nx2,ny2
                input bboxes
            reg:  numpy array, shape n x 4  rnet预测的box的offset 用于还原GT (offset_x1*size)+nx1 ...
                bboxes adjustment
        Returns:
        -------
            bboxes after refinement
        """

        bbox_c = bbox.copy()
        w = bbox[:, 2] - bbox[:, 0] + 1
        w = np.expand_dims(w, 1)
        h = bbox[:, 3] - bbox[:, 1] + 1
        h = np.expand_dims(h, 1)
        reg_m = np.hstack([w, h, w, h])
        aug = reg_m * reg
        bbox_c[:, 0:4] = bbox_c[:, 0:4] + aug
        return bbox_c

    def generate_bbox(self,cls_pred,box_pred,scale, threshold):
        #TODO 确保cls_pred得到的是softmax概率 第一次PNet概率输出没有加上softmax
        '''
        cls_pred: tensor(1,2,H,W) detect score for each position
        box_pred:     tensor(1,4,H,W)  bbox
        '''
        stride = 2
        cellsize = 12
        #向量的处理
        #(1,2,H,W)-(1,2,H,W)softmax-(2,H,W)--(H,W,2)--(H,W)
        face_pred=cls_pred[0,...].permute(1,2,0)[...,1]
        # face_pred=cls_pred[0,...].permute(1,2,0)           #(1,2,H,W)--(2,H,W)--(H,W,2)
        # face_pred=F.softmax(face_pred,dim=2)[...,1]
        box_pred=box_pred[0,...].permute(1,2,0)#(1,4,H,W)--(4,H,W)--(H,W,4)
        #获取满足要求的mask
        index=torch.ge(face_pred,threshold)
        #获取mask,所在的坐标，相当于feature map上满足要求的坐标值，用于转换到原图  torch.LongTensor-->torch.FloatTensor
        fm_index=torch.nonzero(index).float()
        if fm_index.shape[0] == 0:#[num,2]
            return torch.tensor([])
        #获取满足要求的boundingbox的预测的偏移坐标,以及预测结果
        face_pred=face_pred[index]#[num,]
        box_pred_keep=box_pred[index]#[num,4]
        # print("111")
        #获取满足要求的feature map像素坐标 在原图的左上角坐标 x1,y1
        ori_x1y1=torch.round((fm_index*stride)/scale)#[num,2]
        #获取满足要求的feature map像素坐标 在原图的右下角坐标 x2,y2
        ori_x2y2=torch.round((fm_index*stride+cellsize)/scale)
        #获取满足要求的预测概率
        face_pred=face_pred.unsqueeze(0).reshape(-1,1)# #[num,]--[1,num]--[num,1]
        res=torch.cat((ori_x1y1,ori_x2y2,face_pred,box_pred_keep),dim=1)#[num,9]
        return res.cpu().detach().numpy()

    def np_generate_bbox(self, cls_map, reg, scale, threshold):
        """
            generate bbox from feature cls_map according to the threshold
        Parameters:
        ----------
            cls_map: tensor(1,2,H,W) detect score for each position
            reg:     tensor(1,4,H,W)  bbox
            scale: float number
                scale of this detection
            threshold: float number
                detect threshold
        Returns:
        -------
            bbox array num*9(x1,y1,x2,y2,score,x1_offset,y1_offset,x2_offset,y2_offset)
        """
        cls_map_view=cls_map[0,:,:,:]#tensor(2,H,W)
        # print(cls_map_view[:,0:10,0:10])
        cls_map_view=cls_map_view[1,:,:]#tensor(H,W)
        # print(cls_map_view[0:10,0:10])
        cls_map_view=cls_map_view.cpu().detach().numpy() #numpy (H W)
        # print("cls_map_view size:",cls_map_view.shape)#cls_map_view size: (99, 140)
        # index of class_prob larger than threshold
        t_index = np.where(cls_map_view > threshold)#p:0.6 t_index为 numpy
        #t_index[0]为所有cls_map_view中所有满足要求的H索引
        #t_index[1]为所有cls_map_view中所有满足要求的W索引

        reg=reg[0,:,:,:]##tensor(4,H,W)
        reg=reg.cpu().detach().numpy()#numpy (4,H,W)
        reg=reg.transpose((1,2,0))#numpy (H,W,4)
        #print("reg size:",reg.shape) (99, 140, 4)
        # reg_np=reg_np.swapaxes(1,0)#numpy (H,4,W)
        # reg_np=reg_np.swapaxes(2,1)#numpy (H,W,4)
        # 所有的特征图的阈值都不满足
        if t_index[0].size == 0:
            return np.array([])

        # offset
        stride = 2
        cellsize = 12
        dx1, dy1, dx2, dy2 = [reg[t_index[0], t_index[1], i] for i in range(4)]
        reg_np = np.array([dx1, dy1, dx2, dy2])

        score = cls_map_view[t_index[0], t_index[1]]
        boundingbox = np.vstack([np.round((stride * t_index[1]) / scale),#原图boundingbox nx1   boundingbox特征图一个点对应原图的区域
                                 np.round((stride * t_index[0]) / scale),#原图boundingbox ny1
                                 np.round((stride * t_index[1] + cellsize) / scale),#原图boundingbox nx2
                                 np.round((stride * t_index[0] + cellsize) / scale),#原图boundingbox ny2
                                 score,#原图boundingbox 预测的得分
                                 reg_np])#原图boundingbox 预测的偏移 （x1-nx1）/size  其中size=（nx2-nx1,ny2-ny1)
        #print("size,",boundingbox.shape)#(9, 91689)
        return boundingbox.T#(91689,9)

    # pre-process images
    # img_resized numpy array (h,w,c)bgr
    # img_tensor tensor (1,c,h,w) rgb
    def processed_image(self, img, scale):
        # print(img.shape,scale)
        height, width, channels = img.shape
        new_height = int(height * scale)  # resized new height
        new_width = int(width * scale)  # resized new width
        new_dim = (new_width, new_height)
        img_resized = cv2.resize(img, new_dim, interpolation=cv2.INTER_LINEAR)  # resized image hwc bgr

        data_transforms =transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
        # print(img_resized[0,0,:])
        # img_tensor=img_resized
        img_tensor=img_resized[:,:,[2,1,0]]#hwc rgb
        # print(img_tensor[0,0,:])
        img_tensor=data_transforms(img_tensor)#获取该图片的pytorch tensor [3,H,W]
        img_tensor=img_tensor.unsqueeze(0)#[1,3,H,W]
        # print(img_tensor[0,:,0,0])
        return img_resized,img_tensor

    #从pnet 的网络得到的 boxes进行约束后将其进行转换得到tensor
    def rnet_processed_image(self, img):
        ret=True
        try:
            data_transforms =transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])

            size=(24,24)
            img_resized = cv2.resize(img, size, interpolation=cv2.INTER_LINEAR)  # resized image hwc bgr

            img_tensor=img_resized[:,:,[2,1,0]]#hwc rgb
            img_tensor=data_transforms(img_tensor)#获取该图片的pytorch tensor [3,24,24] chw rgb
            img_tensor=img_tensor.unsqueeze(0)#[1,3,24,24] chw rgb
        except :
            ret=False
            img_tensor=torch.zeros([])
        # print(img_tensor[0,:,0,0])
        return ret,img_tensor

    def onet_processed_image(self, img):
        ret=True
        try:
            data_transforms =transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])

            size=(48,48)
            img_resized = cv2.resize(img, size, interpolation=cv2.INTER_LINEAR)  # resized image hwc bgr

            img_tensor=img_resized[:,:,[2,1,0]]#hwc rgb
            img_tensor=data_transforms(img_tensor)#获取该图片的pytorch tensor [3,24,24] chw rgb
            img_tensor=img_tensor.unsqueeze(0)#[1,3,24,24] chw rgb
        except :
            ret=False
            img_tensor=torch.zeros([])
        # print(img_tensor[0,:,0,0])
        return ret,img_tensor

    #
    def pad(self, bboxes, w, h):
        """
            pad the the bboxes, also restrict the size of it
        Parameters:
        ----------
            bboxes: numpy array, n x 5
                input bboxes
            w: float number
                width of the input image
            h: float number
                height of the input image
        Returns :
        ------
            dy, dx : numpy array, n x 1
                start point of the bbox in target image
            edy, edx : numpy array, n x 1
                end point of the bbox in target image
            y, x : numpy array, n x 1
                start point of the bbox in original image
            ey, ex : numpy array, n x 1
                end point of the bbox in original image
            tmph, tmpw: numpy array, n x 1
                height and width of the bbox
        """
        #tmpw, tmph box的宽和高
        tmpw, tmph = bboxes[:, 2] - bboxes[:, 0] + 1, bboxes[:, 3] - bboxes[:, 1] + 1
        num_box = bboxes.shape[0]

        dx, dy = np.zeros((num_box,)), np.zeros((num_box,))

        #edx, edy box的 w-1 和 h-1
        edx, edy = tmpw.copy() - 1, tmph.copy() - 1

        # x, y, ex, ey box的x1 y1 x2 y2
        x, y, ex, ey = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]

        #如果boxes的x2->ex 超过了 原图的右下角坐标的x2->w-1  则boxes的宽为原图的右下角x2->w-1 减去 box的左上角的x1
        tmp_index = np.where(ex > w - 1)
        edx[tmp_index] = tmpw[tmp_index] + w - 2 - ex[tmp_index]
        #edx[tmp_index] = (w-1)+ ((tmpw[tmp_index]-1)-ex[tmp_index])
        ex[tmp_index] = w - 1

        tmp_index = np.where(ey > h - 1)
        edy[tmp_index] = tmph[tmp_index] + h - 2 - ey[tmp_index]
        ey[tmp_index] = h - 1

        tmp_index = np.where(x < 0)
        dx[tmp_index] = 0 - x[tmp_index]
        x[tmp_index] = 0

        tmp_index = np.where(y < 0)
        dy[tmp_index] = 0 - y[tmp_index]
        y[tmp_index] = 0

        return_list = [dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph]
        return_list = [item.astype(np.int32) for item in return_list]

        return return_list

    def detect_pnet(self, im):
        """Get face candidates through pnet

        Parameters:
        ----------
        im: numpy array [h,w,3]
            input image array

        Returns:
        -------
        boxes: numpy array
            detected boxes before calibration
        boxes_c: numpy array
            boxes after calibration
        """
        net_size = 12
        self.pnet_detector.eval()
        current_scale = float(net_size) / self.min_face_size  # find initial scale 0.6
        # print("current_scale", net_size, self.min_face_size, current_scale)
        im_resized,img_tensor = self.processed_image(im, current_scale)
        current_height, current_width, _ = im_resized.shape
        all_boxes = list()
        # print("开始图像金字塔！！")
        while min(current_height, current_width) > 12:#图像金字塔
            t1=time.time()

            # return the result predicted by pnetim_resized
            # cls_pred : H*w*2  (1,3,12,12)-->(1,2,1,1)  (1,im_resized3,current_height,current_width)-->(1,2,current_height-12,current_width-12) stride=1
            # box_pred: H*w*4   (1,3,12,12)-->(1,4,1,1)  (1,im_resized3,current_height,current_width)-->(1,4,current_height-12,current_width-12)
            # class_prob and bbox_pred
            if self.use_gpu:
                img_tensor=img_tensor.cuda()
            cls_pred, box_pred,_ = self.pnet_detector(img_tensor.cuda())
            # print(cls_pred[0,...].permute(1,2,0)[0:2,0:2,:])
            cls_pred=F.softmax(cls_pred,dim=1)
            # print(cls_pred[0,...].permute(1,2,0)[0:2,0:2,:])
            t_net=time.time()
            #print("t_net-t1:",t_net-t1)
            # boxes: tensor[num,9(x1,y1,x2,y2,score,x1_offset,y1_offset,x2_offset,y2_offset)]
            # print("current_scale:",current_scale,"current_height:",current_height,"current_width:",current_width)
            boxes = self.np_generate_bbox(cls_pred, box_pred, current_scale,self.thresh[0])
            t2=time.time()

            #print("t2-t_net:",t2-t_net)
            # scale_factor is 0.79 in default
            current_scale *= self.scale_factor
            im_resized,img_tensor= self.processed_image(im, current_scale)#big bug im而不是im_resized
            current_height, current_width, _ = im_resized.shape
            t3=time.time()
            #print("t3-t2:",t3-t2)
            if boxes.shape[0] == 0:
                continue
            # get the index from non-maximum s
            # keep = torch_nms(torch.from_numpy(boxes[:, :4]), torch.from_numpy(boxes[:, 4]),0.5)
            # print("PNet {} boxes before nms 0.5".format(boxes.shape[0]))
            keep = py_nms(boxes[:, :5], 0.5, 'Union')
            boxes = boxes[keep]

            ###########################
            # 验证feature_map对应原图是否正确
            # im_show=im.copy()
            # cv2.imshow("mtcnn",im_show)
            # print("PNet {} boxes after nms 0.5",boxes.shape[0])
            # # print(boxes[0,1])
            # cv2.waitKey(10)
            # for i in range(boxes.shape[0]):
            #     x1=int(max(boxes[i,0],0))
            #     y1=int(max(boxes[i,1],0))
            #     x2=int(min(boxes[i,2],im_show.shape[1]))
            #     y2=int(min(boxes[i,3],im_show.shape[0]))
            #     cv2.rectangle(im_show,(x1,y1),(x2,y2),(255,0,0),1)
            #     cv2.imshow("mtcnn",im_show)
            #     if cv2.waitKey(1) == ord('q'):
            #             break
            # print("draw finish")
            # cv2.waitKey(100)
            ###########################

            all_boxes.append(boxes)
            t4=time.time()
            # print("t4-t3:{}".format(t4-t3))
        if len(all_boxes) == 0:
            return None, None, None

        all_boxes = np.vstack(all_boxes)

        # merge the detection from first stage
        keep = py_nms(all_boxes[:, 0:5], 0.7, 'Union')
        all_boxes = all_boxes[keep]
        # print("PNet {} boxes after nms 0.7".format(all_boxes.shape[0]))

        boxes = all_boxes[:, :5]
        bbw = all_boxes[:, 2] - all_boxes[:, 0] + 1
        bbh = all_boxes[:, 3] - all_boxes[:, 1] + 1

        # refine the boxes  根据预测的偏移与boundingbox求GT
        boxes_c = np.vstack([all_boxes[:, 0] + all_boxes[:, 5] * bbw,
                             all_boxes[:, 1] + all_boxes[:, 6] * bbh,
                             all_boxes[:, 2] + all_boxes[:, 7] * bbw,
                             all_boxes[:, 3] + all_boxes[:, 8] * bbh,
                             all_boxes[:, 4]])
        boxes_c = boxes_c.T

        return boxes, boxes_c, None

    #rnet 网络检测 图片经过pnet后 得到了预测为人脸的boxes坐标[num,5(x1,y1,x2,y2,score)]
    #根据boxes:将boxes转换为方形，从原图中截取相应的图片进行resize(24,24)放进训练好的rnet网络中进行预测，最终得到满足要求的boxes[num,5]
    def detect_rnet(self,im, dets):
        """Get face candidates using rnet

        Parameters:
        ----------
        im: numpy array
            input image array
        dets: numpy array
            detection results of pnet

        Returns:
        -------
        boxes: numpy array
            detected boxes before calibration
        boxes_c: numpy array
            boxes after calibration
        """
        h, w, c = im.shape
        dets = self.convert_to_square(dets)
        dets[:, 0:4] = np.round(dets[:, 0:4])

        [dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph] = self.pad(dets, w, h)
        num_boxes = dets.shape[0]
        # cropped_ims = np.zeros((num_boxes, 24, 24, 3), dtype=np.float32)
        cropped_ims = torch.zeros((num_boxes, 3, 24, 24), dtype=torch.float32)
        #根据boxes从原图中截取图片并且resize成24
        print("num_boxes:",num_boxes)
        for i in range(num_boxes):
            #过滤掉最小人脸
            #if tmph[i]<self.min_face_size or tmpw[i]<self.min_face_size or x[i] < 0 or y[i] < 0 or ex[i] > w - 1 or ey[i] > h - 1:
            if tmph[i]<5 or tmpw[i]<5:
                # print("error {}".format(imgpath))
                print("error {} {} {}".format(tmph[i],tmpw[i],dets[i]))
                continue
            tmp = np.zeros((tmph[i], tmpw[i], 3), dtype=np.uint8)
            tmp[dy[i]:edy[i] + 1, dx[i]:edx[i] + 1, :] = im[y[i]:ey[i] + 1, x[i]:ex[i] + 1, :]
            if tmp.shape[0]<=0 or tmp.shape[1]<=0:
                continue
            ret,rnet_tensor=self.rnet_processed_image(tmp)
            if ret==False:
                # print("throw:",imgpath)
                print("throw:",im.shape,tmp.shape)
                continue
            cropped_ims[i, :, :, :] = rnet_tensor
        print("cropped_ims shape:",cropped_ims.shape)
        #cropped_ims [num 3 24 24]
        # cls_scores : num_data*2
        # reg: num_data*4
        # landmark: num_data*10
        #cls_scores [num,2]  reg [num,4]
        if self.use_gpu:
            cropped_ims=cropped_ims.cuda()
        cls_scores, reg, _ = self.rnet_detector(cropped_ims)
        cls_scores=F.softmax(cls_scores,dim=1)

        # torch to np array
        cls_scores=cls_scores.cpu().detach().numpy()
        reg=reg.cpu().detach().numpy()
        print("rnet cls_scores shape:",cls_scores.shape)
        cls_scores = cls_scores[:, 1]#np [num,]
        keep_inds = np.where(cls_scores > self.thresh[1])[0]
        if len(keep_inds) > 0:
            boxes = dets[keep_inds]
            boxes[:, 4] = cls_scores[keep_inds]
            reg = reg[keep_inds]
            # landmark = landmark[keep_inds]
        else:
            return None, None, None

        keep = py_nms(boxes, 0.6)
        boxes = boxes[keep]
        boxes_c = self.calibrate_box(boxes, reg[keep])
        return boxes, boxes_c, None

    def detect_onet(self, im, dets):
        """Get face candidates using onet

        Parameters:
        ----------
        im: numpy array
            input image array
        dets: numpy array
            detection results of rnet

        Returns:
        -------
        boxes: numpy array
            detected boxes before calibration
        boxes_c: numpy array
            boxes after calibration
        """
        h, w, c = im.shape
        dets = self.convert_to_square(dets)
        dets[:, 0:4] = np.round(dets[:, 0:4])
        [dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph] = self.pad(dets, w, h)
        num_boxes = dets.shape[0]
        cropped_ims = torch.zeros((num_boxes, 3, 48, 48), dtype=torch.float32)
        for i in range(num_boxes):
            if tmph[i]<5 or tmpw[i]<5:
                # print("error {}".format(imgpath))
                print("error {} {} {}".format(tmph[i],tmpw[i],dets[i]))
                continue
            tmp = np.zeros((tmph[i], tmpw[i], 3), dtype=np.uint8)
            tmp[dy[i]:edy[i] + 1, dx[i]:edx[i] + 1, :] = im[y[i]:ey[i] + 1, x[i]:ex[i] + 1, :]
            if tmp.shape[0]<=0 or tmp.shape[1]<=0:
                continue
            ret,onet_tensor=self.onet_processed_image(tmp)
            if ret==False:
                # print("throw:",imgpath)
                print("throw:",im.shape,tmp.shape)
                continue
            cropped_ims[i, :, :, :] = onet_tensor

        if self.use_gpu:
            cropped_ims=cropped_ims.cuda()
        cls_scores, reg, landmark = self.onet_detector(cropped_ims)
        cls_scores=F.softmax(cls_scores,dim=1)
        cls_scores=cls_scores.cpu().detach().numpy()
        reg=reg.cpu().detach().numpy()
        landmark=landmark.cpu().detach().numpy()
        # prob belongs to face
        cls_scores = cls_scores[:, 1]
        keep_inds = np.where(cls_scores > self.thresh[2])[0]
        if len(keep_inds) > 0:
            # pickout filtered box
            boxes = dets[keep_inds]
            boxes[:, 4] = cls_scores[keep_inds]
            reg = reg[keep_inds]
            landmark = landmark[keep_inds]
        else:
            return None, None, None

        # width
        w = boxes[:, 2] - boxes[:, 0] + 1
        # height
        h = boxes[:, 3] - boxes[:, 1] + 1
        landmark[:, 0::2] = (np.tile(w, (5, 1)) * landmark[:, 0::2].T + np.tile(boxes[:, 0], (5, 1)) - 1).T
        landmark[:, 1::2] = (np.tile(h, (5, 1)) * landmark[:, 1::2].T + np.tile(boxes[:, 1], (5, 1)) - 1).T
        boxes_c = self.calibrate_box(boxes, reg)

        boxes = boxes[py_nms(boxes, 0.6, "Minimum")]
        keep = py_nms(boxes_c, 0.6, "Minimum")
        boxes_c = boxes_c[keep]
        landmark = landmark[keep]
        return boxes, boxes_c, landmark

    # use for video
    def detect(self, img):
        """Detect face over image
        """
        boxes = None
        t = time.time()

        # pnet
        t1 = 0
        if self.pnet_detector:
            boxes, boxes_c, _ = self.detect_pnet(img)
            if boxes_c is None:
                return np.array([]), np.array([])
            t1 = time.time() - t
            t = time.time()
        print("pnet boxes shape:",boxes_c.shape)
        # rnet
        t2 = 0
        if self.rnet_detector:
            boxes, boxes_c, _ = self.detect_rnet(img, boxes_c)
            if boxes_c is None:
                return np.array([]), np.array([])

            t2 = time.time() - t
            t = time.time()

        # onet
        t3 = 0
        if self.onet_detector:
            boxes, boxes_c, landmark = self.detect_onet(img, boxes_c)
            if boxes_c is None:
                return np.array([]), np.array([])

            t3 = time.time() - t
            t = time.time()
            # print(
            #    "time cost " + '{:.3f}'.format(t1 + t2 + t3) + '  pnet {:.3f}  rnet {:.3f}  onet {:.3f}'.format(t1, t2,
            #                                                                                t3))

        return boxes_c, landmark

    #test_data 数据读取器 (1,h,w,3)--tensor 需要转化成numpy opencv的数据格式  可传入网络 batch_size==1
    #test_size 数据总的长度
    #all_boxes [ ] 长度为wider图片数据集的个数,每个元素为给图片对应的预测box [num,5] 5:x1,y1,x2,y2,score
    def detect_face(self, test_data,test_size):
        all_boxes = []  # save each image's bboxes
        landmarks = []

        sum_time = 0
        t1_sum = 0
        t2_sum = 0
        t3_sum = 0

        empty_array = np.array([])
        s_time = time.time()
        print_freq=100
        for batch_idx,img in enumerate(test_data):
            # print(imgpath)
            if (batch_idx) % print_freq == 0:
                c_time = (time.time() - s_time )/print_freq
                print("{} {}/{} images done".format(datetime.datetime.now(),batch_idx ,test_size))
                print('%f seconds for each image' % c_time)
                s_time = time.time()
            # print(databatch.shape)
            # print(databatch[0,0,:])
            # sys.eixt()
            # img=databatch.squeeze().numpy()#获得numpy 图片数组 h w c bgr
            # img=cv2.imread(imgpath)#获得numpy 图片数组 h w c bgr
            # print(img.shape)
            # print(img[0,0,:])
            #databatch图片送入pnet网络
            if self.pnet_detector:
                st = time.time()
                # ignore landmark boxes_c 每张图片所有的阈值>0.6 的box shape=(num,5) x1,y1,x2,y2,score
                boxes, boxes_c, landmark = self.detect_pnet(img)
                t1 = time.time() - st
                sum_time += t1
                t1_sum += t1
            if boxes_c is None:
                print("boxes_c is None...")
                all_boxes.append(empty_array)
                # pay attention
                landmarks.append(empty_array)
                continue
                #print(all_boxes)
            # rnet
            if self.rnet_detector is not None:
                t = time.time()
                boxes, boxes_c, landmark = self.detect_rnet(img, boxes_c)
                t2 = time.time() - t
                sum_time += t2
                t2_sum += t2
                if boxes_c is None:
                    all_boxes.append(empty_array)
                    landmarks.append(empty_array)
                    continue

            # onet
            if self.onet_detector:
                t = time.time()
                boxes, boxes_c, landmark = self.detect_onet(databatch, boxes_c)
                t3 = time.time() - t
                sum_time += t3
                t3_sum += t3
                if boxes_c is None:
                    all_boxes.append(empty_array)
                    landmarks.append(empty_array)
                    continue
            # print("{} PNet gen {} boxes".format(imgpath,boxes_c.shape[0]))
            all_boxes.append(boxes_c)
            landmark = [1]
            landmarks.append(landmark)
        print('num of images', test_size)
        print("time cost in average" +
            '{:.3f}'.format(sum_time/test_size) +
            '  pnet {:.3f}  rnet {:.3f}  onet {:.3f}'.format(t1_sum/test_size, t2_sum/test_size,t3_sum/test_size))
        # num_of_data*9,num_of_data*10
        print('boxes length:',len(all_boxes))
        return all_boxes, landmarks

    def detect_single_image(self, im):
        all_boxes = []  # save each image's bboxes

        landmarks = []

       # sum_time = 0

        t1 = 0
        if self.pnet_detector:
          #  t = time.time()
            # ignore landmark
            boxes, boxes_c, landmark = self.detect_pnet(im)
           # t1 = time.time() - t
           # sum_time += t1
            if boxes_c is None:
                print("boxes_c is None...")
                all_boxes.append(np.array([]))
                # pay attention
                landmarks.append(np.array([]))

        # rnet

        if boxes_c is None:
            print('boxes_c is None after Pnet')
        t2 = 0
        if self.rnet_detector and not boxes_c is  None:
           # t = time.time()
            # ignore landmark
            boxes, boxes_c, landmark = self.detect_rnet(im, boxes_c)
           # t2 = time.time() - t
           # sum_time += t2
            if boxes_c is None:
                all_boxes.append(np.array([]))
                landmarks.append(np.array([]))

        # onet
        t3 = 0
        if boxes_c is None:
            print('boxes_c is None after Rnet')

        if self.onet_detector and not boxes_c is  None:
          #  t = time.time()
            boxes, boxes_c, landmark = self.detect_onet(im, boxes_c)
         #   t3 = time.time() - t
          #  sum_time += t3
            if boxes_c is None:
                all_boxes.append(np.array([]))
                landmarks.append(np.array([]))

        #print(
         #   "time cost " + '{:.3f}'.format(sum_time) + '  pnet {:.3f}  rnet {:.3f}  onet {:.3f}'.format(t1, t2, t3))

        all_boxes.append(boxes_c)
        landmarks.append(landmark)

        return all_boxes, landmarks


import requests
from bs4 import BeautifulSoup
from django.shortcuts import render
from .models import Search

# Create your views here.
AMAZONE_URL = 'https://www.amazon.in/s?k={}'
FLIPKART_URL = 'https://www.flipkart.com/search?q={}'

def index(request):
    return render(request, 'home/index.html')

def new_search(request):
    search = request.POST.get('search')
    # Search.objects.create(search=search)

    def amazon(search):
        FINAL_AMAZON_URL = AMAZONE_URL.format(
            requests.compat.quote_plus(search))
        response = requests.get(FINAL_AMAZON_URL)
        soup = BeautifulSoup(response.text, features="html.parser")
        search_title = soup.find_all(
            'span', {'class': 'a-size-medium a-color-base a-text-normal'})
        search_price = soup.find_all(
            'span', {'class': 'a-price-whole'})
        search_link = soup.find_all(
            'a', {'class': 'a-link-normal a-text-normal'})
        search_photo = soup.find_all('img', {'class': 's-image'})
        amazon_title = search_title[0].text
        amazon_price = search_price[0].text
        amazon_url = f"https://www.amazon.in{search_link[0].get('href')}"
        amazon_photo = search_photo[0].get('src')
        return {'amazon_title': amazon_title,
                'amazon_price': amazon_price,
                'amazon_url': amazon_url,
                'amazon_photo': amazon_photo}

    def flipkart(search):
        FINAL_FLIPKART_URL = FLIPKART_URL.format(
            requests.compat.quote_plus(search))
        response = requests.get(FINAL_FLIPKART_URL)
        soup = BeautifulSoup(response.text, features="html.parser")
        search_title = soup.find_all(
            'div', {'class': '_4rR01T'})
        search_price = soup.find_all(
            'div', {'class': '_30jeq3 _1_WHN1'})
        search_link = soup.find_all(
            'a', {'class': '_1fQZEK'})
        flipkart_title = search_title[0].text
        flipkart_price = search_price[0].text
        flipkart_url = f"https://www.flipkart.com{search_link[0].get('href')}"
        return {'flipkart_title': flipkart_title,
                'flipkart_price': flipkart_price,
                'flipkart_url': flipkart_url}
    context = {
        'search': search,
    }
    context.update(amazon(search))
    context.update(flipkart(search))
    print(context)
    return render(request, 'home/new_search.html', context)


#-*- coding: utf-8 -*-
import sys
import os
import time
import requests

#reload(sys)
#sys.setdefaultencoding('utf-8')

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.alert import Alert
from bs4 import BeautifulSoup

def bus_chk( start_id, dest_id, date) :
	print(start_id)
	print(dest_id)
	print(date)

	options = webdriver.ChromeOptions()
	options.add_argument('headless')
	options.add_argument('window-size=1920x1080')
	options.add_argument("disable-gpu")
	# 혹은 options.add_argument("--disable-gpu")

	ff_driver = webdriver.Chrome('/Users/peten/lib/chromedriver', chrome_options=options)

	#ff_driver = webdriver.Chrome('/Users/peten/lib/chromedriver')
	ff_driver.implicitly_wait(1)

	ff_driver.get("https://www.kobus.co.kr/main.do")

	#팝업메뉴제거
	elements = ff_driver.find_elements_by_css_selector("a[class='pop_close']")
	for i in elements:
		i.click()

	#버스 조회

	'''

	<input type="hidden" name="deprDtm" id="deprDtm" value="20190415">
	<input type="hidden" name="arvlDtm" id="arvlDtm" value="20190415">

	<input type="hidden" name="deprDtmAll" id="deprDtmAll" value="2019. 4. 15. 목">
	<input type="hidden" name="arvlDtmAll" id="arvlDtmAll" value="2019. 4. 15. 목">

	'''
	ff_driver.execute_script("document.getElementById('deprDtm').value = '20190517';")
	#ff_driver.execute_script("document.getElementById('arvlDtm').value = '20190228';")
	ff_driver.execute_script("document.getElementById('deprDtmAll').value='2019. 5. 17. 금';")
	#ff_driver.execute_script("document.getElementById('arvlDtmAll').value='2019. 3. 1. 금';")

	ff_driver.execute_script("fnReadDeprInfoList(event);")
	ff_driver.execute_script("fnDeprChc('010','서울경부');")
	ff_driver.execute_script("fnArvlChc('810','구미','N','','','180');")

	ff_driver.execute_script("fnDeprChc('810','구미');") #출발지
	ff_driver.execute_script("fnArvlChc('010','서울경부','N','','','240');") #도착지

	#ff_driver.execute_script("fnYyDtmStup(1,'text_date1','0');")
	ff_driver.execute_script("fnRotVldChc();")
	time.sleep(2)

	chk = 0
	try :
		alert = ff_driver.switch_to.alert
		alert.accept()
		ff_driver.quit()
		print('program end')
		chk = 1
	except :
		print('next')

	if chk == 1:
		exit()

	time.sleep(3)

	'''
	이제 각 터미널 번호와 터미널 이름을 매칭시켜 DB에 집어넣고
	DB를 구축해서 DB에 결과값을 집어넣는거 까지 하면 끝;

	몇자리 남았는지는 어떻게 확인하지??? (이건 사용자가 요청하면 확인하는 걸로;;)

	'''

	'''

	fnAlcnSrchBef();
	fnPopFeeAgrm();

	print(ff_driver.page_source)

	'''

	p_page = ff_driver.page_source
	ff_driver.quit()

	soup = BeautifulSoup(p_page, 'html.parser')
	notices = soup.select('div.bustime_wrap > div.bus_time > p > a > span.start_time')
	len_soup = len(notices)

	if len_soup == 0:
		print("조회 결과 :: 없음")
	else :
		print("select number : ", len_soup)

	for n in notices:
		print(n.text.strip())

x = input('Input x : ')
y = input('Input y : ')
z = input('Input date : ')
bus_chk(x,y,z)


# -*- coding: utf-8 -*-
"""
Created on Wed Mar 10 17:46:19 2021

@author: Ben
"""

import pandas as pd
import seaborn as sns
from scipy.stats import pearsonr
from scipy.stats import spearmanr
import matplotlib.pyplot as plt

from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from numpy import cov
from sklearn.preprocessing import StandardScaler

from sklearn.ensemble import RandomForestClassifier
from sklearn import svm
from sklearn import metrics
from sklearn.metrics import accuracy_score
import numpy as np
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import KFold
students_df = pd.read_csv('with_grades_df_2020_2021.csv')
students_df.rename( columns={'Unnamed: 0':'anonymous_id'}, inplace=True )
#%%##### Grade Distribution
def plot_grades(df):
    grades = df['OVERALL_GRADE'].value_counts()
    grades.plot.bar()
plot_grades(students_df)

course_views = students_df['Study Area: [Module 2020/2021] CM4107 - Full Time: Advanced Artificial Intelligence'];
gradeDictionary = {5:'A',4:'B',3:'C',2:'D',1:'E',0:'F'}

#%%###### TARGET LABEL ENCODER
l1 = LabelEncoder()

#fit will transform labels into a numeric value
l1.fit(students_df['OVERALL_GRADE'])

#transform the existing student_df dataset
students_df.OVERALL_GRADE = l1.transform(students_df.OVERALL_GRADE);
######

## intuitive transform
## reverse the label encode values
## previously 0=A, transform 0=NS, 1=E etc.
for idx, val in enumerate(students_df['OVERALL_GRADE']):
    if(val == 6):
        #print("grade is F")
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
        #print("grade:",val,"\n")
        #print("current student df:",val,"\n")
        #print("current grade label:",5,"\n")
        students_df.at[idx, 'OVERALL_GRADE'] = 0
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
    elif(val == 5):
        #print("grade is NS")
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
        #print("grade:",val,"\n")
        #print("current student df:",val,"\n")
        #print("current grade label:",5,"\n")
        students_df.at[idx, 'OVERALL_GRADE'] = 0
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
    elif(val == 4):
        #print("grade is E")
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
        #print("grade:",val,"\n")
        #print("current student df:",val,"\n")
        #print("current grade label:",4,"\n")
        students_df.at[idx, 'OVERALL_GRADE'] = 1
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
    elif(val == 3):
        #print("grade is D")
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
        #print("grade:",val,"\n")
        #print("current student df:",val,"\n")
        #print("current grade label:",3,"\n")
        students_df.at[idx, 'OVERALL_GRADE'] = 2
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
    elif(val == 2):
        #print("grade is C")
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
        #print("grade:",val,"\n")
        #print("current student df:",val,"\n")
        #print("current grade label:",2,"\n")
        students_df.at[idx, 'OVERALL_GRADE'] = 3
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
    elif(val == 1):
        #print("grade is B")
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
        #print("grade:",val,"\n")
        #print("current student df:",val,"\n")
        #print("current grade label:",1,"\n")
        students_df.at[idx, 'OVERALL_GRADE'] = 4
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
    elif(val == 0):
        #print("grade is A")
        #print(students_df.loc[idx]['OVERALL_GRADE'],"\n")
        #print("grade:",val,"\n")
        #print("current student df:",val,"\n")
        #print("current grade label:",0,"\n")
        students_df.at[idx, 'OVERALL_GRADE'] = 5
        #print("new label:", students_df.loc[idx]['OVERALL_GRADE'],"\n")

#%%###### Course Views Logistic Regression
course_grades = pd.concat([course_views,students_df['OVERALL_GRADE']],axis='columns')
course_grades = course_grades.rename(columns={'Study Area: [Module 2020/2021] CM4107 - Full Time: Advanced Artificial Intelligence':'course_views'})

covariance = cov(course_grades['course_views'],course_grades['OVERALL_GRADE'])
print(covariance)
#positive covariance shows there is a linear relationship

#pearson shows there is a moderate linear relationship
course_corr, _ = pearsonr(course_views, course_grades['OVERALL_GRADE'])
print("pearson course views linear relation with grade", course_corr)

##Two variables may be related by a nonlinear relationship, such that the relationship is stronger or weaker across the distribution of the variables.
course_views_spearman, _ = spearmanr(course_grades['course_views'],course_grades['OVERALL_GRADE'])
print('Spearmans correlation: %.3f' % course_views_spearman)

#%% Find the corelation for every feature
def get_correlations(students_df):
    features = students_df.drop(['anonymous_id','COURSEWORK_1','COURSEWORK_2','OVERALL_GRADE'], axis=1)
    #for each feature in students_df
    corelations = list()

    for feature in features:
        feature_extracted = students_df[feature]
        current_grade = students_df['OVERALL_GRADE']
        corr, _ = pearsonr(feature_extracted,current_grade)
        corelations.append([feature,corr])

    corelations = pd.DataFrame(corelations)
    corelations.columns = ['Feature', 'Corelation']

    corelations = corelations.sort_values('Corelation', ascending=False)

    return corelations

corelations = get_correlations(students_df)
#print(corellations.head(20))
#%% Get the columns from correlations and select features from student df
topActivities = corelations.head(20)
topList = list()

for val in topActivities['Feature']:
    topList.append(val)

topList.append('OVERALL_GRADE')
features = students_df[topList]
#print(len(features.index))
#%% Use the Random Forest classifier
X = features.iloc[:,0:20].values
y = features.iloc[:, 20].values

X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2, random_state=42)

sc = StandardScaler()

X_train = sc.fit_transform(X_train)
X_test = sc.fit_transform(X_test)

classifier = RandomForestClassifier(n_estimators=20)
classifier.fit(X_train, y_train)
y_pred = classifier.predict(X_test)

print("Random Forest Accuracy: ",accuracy_score(y_test, y_pred))

#%% SVM
clf = svm.SVC(kernel='linear')
clf.fit(X_train, y_train)

y_pred = clf.predict(X_test)

print("SVM Accuracy:",metrics.accuracy_score(y_test, y_pred))

#%%
X = features.iloc[:,0:len(features.columns)-1].values
y = features.iloc[:, len(features.columns)-1].values
def rf_predict(X,y):
    X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2, random_state=42)
    #print(skf.get_n_splits(X, y))

    param_grid = {
                     'n_estimators': [5, 10, 15, 20, 100],
                     'max_depth': [2, 5, 7, 9, 10, 11]
                 }

    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.fit_transform(X_test)

    random_forest_data=list()
    scores = ['precision', 'recall']

    rfc = RandomForestClassifier(n_jobs=-1,n_estimators=50)
    cv = KFold(n_splits=10, shuffle=True, random_state=42)
    rfc_model = GridSearchCV(estimator=rfc, param_grid=param_grid, cv=cv)
    rfc_model.fit(X_train, y_train)
    print(rfc_model.best_score_)
    print(rfc_model.best_params_)

    rfc_score = rfc_model.score(X_test, y_test)
    print('Test Accuracy: %.3f' % rfc_score)
    return rfc_score

rf_predict(X,y)

def svm_predict(X,y):
    X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2, random_state=42)

    param_grid = [
      {'C': [1, 10, 100, 1000], 'kernel': ['linear']},
      {'C': [1, 10, 100, 1000], 'gamma': [0.001, 0.0001], 'kernel': ['rbf']},
     ]

    sc = StandardScaler()
    X_train = sc.fit_transform(X_train)
    X_test = sc.fit_transform(X_test)
    svc = svm.SVC(kernel='linear')
    cv = KFold(n_splits=10, shuffle=True, random_state=42)
    svc_model = GridSearchCV(estimator=svc, param_grid=param_grid, cv=cv)
    svc_model.fit(X_train, y_train)
    print(svc_model.best_score_)
    print(svc_model.best_params_)

    svm_score = svc_model.score(X_test, y_test)
    print('Test Accuracy: %.3f' % svm_score)

    return svm_score

svm_predict(X,y)

#%%Testing
testFeatures = [10,15,20,25,30,35,40,45,50,55,60,65,70,75,len(students_df.columns)-4]

def best_scores_with_feature_sets(testFeatures):
    all_svms=list();
    all_rfs=list();
    best_df = pd.DataFrame({'Number of Features':testFeatures})
    best_df["Random Forest"] = np.nan
    best_df["Support Vector Classifier"] = np.nan

    micro_df = pd.DataFrame({'Number of Features':testFeatures})
    micro_df["Random Forest"] = np.nan
    micro_df["Support Vector Classifier"] = np.nan

    print(best_df)
    for value in testFeatures:
        topActivities = corelations.head(value)
        topList = list()

        for val in topActivities['Feature']:
            topList.append(val)

        topList.append('OVERALL_GRADE')
        features = students_df[topList]

        X = features.iloc[:,0:value].values
        y = features.iloc[:, value].values

        rf_score = rf_predict(X, y)
        svc_score = svm_predict(X, y)
        #rf_score = rf_predict(X, y)
        #svc_score = svm_predict(X, y)
        #all_svms.append([value,rf_score])
        #all_rfs.append([value,svc_score])

        best_df.loc[best_df['Number of Features'] == value, 'Random Forest'] = rf_score
        best_df.loc[best_df['Number of Features'] == value, 'Support Vector Classifier'] = svc_score

    best_df = best_df.melt('Number of Features', var_name='ML Algorithm on 2020/2021',  value_name='Accuracy')
    sns.set_style("darkgrid")
    sns.factorplot(x="Number of Features", y="Accuracy", hue='ML Algorithm on 2020/2021', data=best_df, palette=sns.color_palette('summer', n_colors=2))
    plt.show()

    return best_df

best_dfs = best_scores_with_feature_sets(testFeatures)


from matplotlib.pyplot import figure, plot, savefig, title, xlabel, ylabel, yticks
from numpy import genfromtxt, linspace
from sys import argv

input_file = str(argv[1])
output_file = str(argv[2])

data = genfromtxt(input_file, delimiter=',')
t = data[0]
norm = data[1]

fig = figure()
title('Norm of the Approximate Solution')
plot(t, norm, '-')
xlabel('t')
ylabel(r"$\Vert u(t)\Vert_{L^2(\mathbb{R}^2;\mathbb{C})}$")
yticks(linspace(0.0, 2.0 * max(norm), 11, endpoint=True))
savefig(output_file)

print('Generated ' + output_file)


# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.6.9 (default, Apr 18 2020, 01:56:04)
# [GCC 8.4.0]
# Embedded file name: /srv/RunestoneComponents/runestone/common/runestonedirective.py
# Compiled at: 2020-04-16 12:40:55
# Size of source mod 2**32: 14669 bytes
__author__ = 'bmiller'
from collections import defaultdict
import binascii, os
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.parsers.rst import Directive
from docutils.utils import get_source_line
from docutils.statemachine import ViewList
from sphinx import application
from sphinx.errors import ExtensionError
UNNUMBERED_DIRECTIVES = [
 'activecode',
 'reveal',
 'video',
 'youtube',
 'vimeo',
 'codelens',
 'showeval',
 'poll',
 'tabbed',
 'tab',
 'timed',
 'disqus']

class RunestoneNode(nodes.Node):
    pass

class Struct:

    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
        args = ['{}={}'.format(k, repr(v)) for k, v in vars(self).items()]
        return 'Struct({})'.format(', '.join(args))

def _get_runestone_data(env):
    if not hasattr(env, 'runestone_data'):
        env.runestone_data = Struct(id_to_page=(dict()),
          page_to_id=(defaultdict(set)))
    return env.runestone_data

def _purge_runestone_data(app, env, docname):
    runestone_data = _get_runestone_data(env)
    for id_ in runestone_data.page_to_id[docname]:
        runestone_data.id_to_page.pop(id_)

    runestone_data.page_to_id.pop(docname)
    env.assesscounter = 0

def _add_autoversion(self, filename):
    for path in self.config._raw_config['html_static_path']:
        full_path = path
        if not os.path.isabs(full_path):
            full_path = os.path.join(self.confdir, full_path)
        if os.path.isdir(path):
            full_path = os.path.join(path, filename)
        else:
            if os.path.normpath(path).endswith(os.path.normpath(filename)):
                full_path = path
            else:
                continue
            try:
                f = open(os.path.join(path, filename), 'rb')
            except IOError:
                continue
            else:
                with f:
                    crc_str = '{:02X}'.format(binascii.crc32(f.read()))
                return '{}?v={}'.format(filename, crc_str)

    raise ExtensionError('Unable to find {} in html_static_path.'.format(filename))

def _add_autoversioned_javascript(self, filename):
    return self.add_javascript(self.add_autoversion(filename))

def _add_autoversioned_stylesheet(self, filename, *args, **kwargs):
    return (self.add_stylesheet)(self.add_autoversion(filename), *args, **kwargs)

application.Sphinx.add_autoversion = _add_autoversion
application.Sphinx.add_autoversioned_javascript = _add_autoversioned_javascript
application.Sphinx.add_autoversioned_stylesheet = _add_autoversioned_stylesheet

def setup(app):
    app.connect('env-purge-doc', _purge_runestone_data)
    app.add_role('skipreading', SkipReading)
    app.add_config_value('runestone_server_side_grading', False, 'env')
    app.add_config_value('generate_component_labels', True, 'env')

class RunestoneDirective(Directive):
    option_spec = {'author':directives.unchanged,
     'tags':directives.unchanged,
     'difficulty':directives.positive_int,
     'autograde':directives.unchanged,
     'practice':directives.unchanged,
     'topics':directives.unchanged}

    def __init__(self, *args, **kwargs):
        (super(RunestoneDirective, self).__init__)(*args, **kwargs)
        env = self.state.document.settings.env
        self.srcpath = env.docname
        split_docname = self.srcpath.split('/')
        if len(split_docname) < 2:
            split_docname.append('')
        self.subchapter = split_docname[(-1)]
        self.chapter = split_docname[(-2)]
        self.basecourse = self.state.document.settings.env.config.html_context.get('basecourse', 'unknown')
        self.options['basecourse'] = self.basecourse
        self.options['chapter'] = self.chapter
        self.options['subchapter'] = self.subchapter

class RunestoneIdDirective(RunestoneDirective):

    def getNumber(self):
        env = self.state.document.settings.env
        if self.name in UNNUMBERED_DIRECTIVES or env.config.generate_component_labels is False:
            return ''
        env.assesscounter += 1
        res = 'Q-%d'
        if hasattr(env, 'assessprefix'):
            res = env.assessprefix + '%d'
        res = res % env.assesscounter
        if hasattr(env, 'assesssuffix'):
            res += env.assesssuffix
        return res

    def updateContent(self):
        if self.content:
            if self.content[0][:2] == '..':
                self.content = ViewList([
                 self.options['qnumber'] + ':', ''], self.content.source(0)) + self.content
            else:
                if self.options['qnumber']:
                    self.content[0] = self.options['qnumber'] + ': ' + self.content[0]

    def run(self):
        if not self.required_arguments >= 1:
            raise AssertionError
        else:
            if 'divid' not in self.options:
                id_ = self.options['divid'] = self.arguments[0]
            else:
                id_ = self.options['divid']
            self.options['qnumber'] = self.getNumber()
            env = self.state.document.settings.env
            runestone_data = _get_runestone_data(env)
            id_to_page = runestone_data.id_to_page
            page_to_id = runestone_data.page_to_id
            if id_ in id_to_page:
                page = id_to_page[id_]
                if page.docname != env.docname or page.lineno != self.lineno:
                    raise self.error('Duplicate ID -- see {}, line {}'.format(page.docname, page.lineno))
                assert id_ in page_to_id[page.docname]
            else:
                id_to_page[id_] = Struct(docname=(env.docname), lineno=(self.lineno))
                page_to_id[env.docname].add(id_)

def first_time(app, *keys):
    key = '$'.join(keys)
    if not hasattr(app, 'runestone_flags'):
        app.runestone_flags = set()
    if key not in app.runestone_flags:
        app.runestone_flags.add(key)
        return True
    return False

def add_i18n_js(app, supported_langs, *i18n_resources):
    if first_time(app, 'add_i18n_js'):
        app.add_autoversioned_javascript('jquery_i18n/CLDRPluralRuleParser.js')
        app.add_autoversioned_javascript('jquery_i18n/jquery.i18n.js')
        app.add_autoversioned_javascript('jquery_i18n/jquery.i18n.messagestore.js')
        app.add_autoversioned_javascript('jquery_i18n/jquery.i18n.fallbacks.js')
        app.add_autoversioned_javascript('jquery_i18n/jquery.i18n.language.js')
        app.add_autoversioned_javascript('jquery_i18n/jquery.i18n.parser.js')
        app.add_autoversioned_javascript('jquery_i18n/jquery.i18n.emitter.js')
        app.add_autoversioned_javascript('jquery_i18n/jquery.i18n.emitter.bidi.js')
    for res in i18n_resources:
        if first_time(app, 'add_i18n_js', res):
            app.add_autoversioned_javascript(res + '.en.js')
            if app.config.language and app.config.language != 'en' and app.config.language in supported_langs:
                app.add_autoversioned_javascript(res + '.' + app.config.language + '.js')

def add_skulpt_js(app):
    if first_time(app, 'add_skulpt_js'):
        app.add_autoversioned_javascript('skulpt.min.js')
        app.add_autoversioned_javascript('skulpt-stdlib.js')
        app.add_javascript('https://cdn.jsdelivr.net/npm/vega@4.0.0-rc.2/build/vega.js')
        app.add_javascript('https://cdn.jsdelivr.net/npm/vega-lite@2.5.0/build/vega-lite.js')
        app.add_javascript('https://cdn.jsdelivr.net/npm/vega-embed@3.14.0/build/vega-embed.js')

def get_node_line(node):
    return get_source_line(node)[1]

def SkipReading(roleName, rawtext, text, lineno, inliner, options={}, content=[]):
    docname = inliner.document.settings.env.docname
    if not hasattr(inliner.document.settings.env, 'skipreading'):
        inliner.document.settings.env.skipreading = set()
    print('ADDING {} to skipreading'.format(docname))
    inliner.document.settings.env.skipreading.add(docname)
    return ([], [])


#!/usr/bin/env python

"""
Plot lipid-lipid g(r) results.
"""

import scipy
import scipy.integrate
import re

class Same:
	"""Make sure items are the same between replicates."""
	def __init__(self): self.this = []
	def add(self,item): self.this.append(item)
	def get(self):
		if not all([self.this[0]==i for i in self.this[1:]]): raise Exception('not same')
		else: return self.this[0]

def make_legend(ax,keys=None):
	# legend at last tile
	legendspec = []
	for sn_general,sns in replicate_mapping:
		if not keys or sn_general in keys:
			legendspec.append(dict(name=extra_labels[sn_general],
				patch=mpl.patches.Rectangle((0,0),1.0,1.0,fc=color_by_simulation(sns[0]))))
	patches,labels = [list(j) for j in zip(*[(i['patch'],i['name']) for i in legendspec])]
	legend = ax.legend(patches,labels,loc='upper left',bbox_to_anchor=(1.0,0.0,1.,1.))
	frame = legend.get_frame()
	frame.set_edgecolor('k')
	frame.set_facecolor('w')
	return legend

def reducer(pairings,pair_residues):
	"""Final reduction in the data."""
	post = dict([])
	for pnum,(pairname,pair) in enumerate(zip(pairings,pair_residues)):
		post[pairname] = {}
		for sn_group,sns_this in replicate_mapping:
			obs,nmols,total_areas = [],Same(),[]
			for sn in sns_this:
				mn = actinlink_monolayer_indexer(sn,abstractor='lipid_chol_com')
				dat = data[sn]['data']
				key = 'counts_mn%d_%s:%s'%(mn,pairname[0],pairname[1])
				if key not in dat:
					status('reducing %s, %s SKIP'%(sn,key))
					continue
				else: status('reducing %s, %s'%(sn,key))
				obs.append(dat[key])
				nmols.add([np.in1d(dat['resnames'][dat['monolayer_indices']==mn],p).sum()
					for p in pair])
				total_areas.append(dat['total_area'])
			if len(obs) not in [0,2]: raise Exception('something has gone wrong')
			elif len(obs)==0: continue
			# average replicates
			counts = np.array(obs).mean(axis=0)
			nmols = nmols.get()
			total_area = np.mean(total_areas) #! total area is averaged between simulations for norming
			density = nmols[0]*nmols[1]/total_area
			post[pairname][sn_group] = dict(counts=counts,total_area=total_area,density=density)

@autoload(plotrun)
def load():
	data,calc = plotload('lipid_rdfs')
	sns = work.sns()
	# generics
	mode = ['summary','cdf'][-1]
	xmax_cumulative = 2.0
	cutoff,binsize = [calc['lipid_rdfs']['calcs']['specs'][i] for i in ['cutoff','binsize']]
	scanrange = np.arange(0,cutoff,binsize)
	middles = (scanrange[1:]+scanrange[:-1])/2
	areas = np.array([np.pi*(binsize)*middles[i]*2 for i in range(len(middles))])
	# collect all possible pairings
	pairings = list(set([tuple(i) for j in [
		data[sn]['data']['pairnames_mn%d'%actinlink_monolayer_indexer(sn,abstractor='lipid_chol_com')]
		for sn in sns] for i in j]))
	pairname_to_pair = dict([(tuple(i),j) for i,j in zip(*[data[sn]['data']['%s_mn%d'%(p,actinlink_monolayer_indexer(sn,abstractor='lipid_chol_com'))] for p in ['pairnames','pairs']])])
	pair_residues = [pairname_to_pair[p] for p in pairings]
	color_by_simulation = actinlink_color_by_simulation
	replicate_mapping = actinlink_replicate_mapping
	extra_labels = actinlink_extra_labels
	#! it would be nice to have this calculation in a separate function but then it will not have globals.
	#! ... this issue needs documenting along with the load magic
	post = dict([])
	for pnum,(pairname,pair) in enumerate(zip(pairings,pair_residues)):
		post[pairname] = {}
		for sn_group,sns_this in replicate_mapping:
			obs,nmols,total_areas = [],Same(),[]
			for sn in sns_this:
				mn = actinlink_monolayer_indexer(sn,abstractor='lipid_chol_com')
				dat = data[sn]['data']
				key = 'counts_mn%d_%s:%s'%(mn,pairname[0],pairname[1])
				if key not in dat:
					status('reducing %s, %s SKIP'%(sn,key))
					continue
				else: status('reducing %s, %s'%(sn,key))
				obs.append(dat[key])
				nmols.add([np.in1d(dat['resnames'][dat['monolayer_indices']==mn],p).sum()
					for p in pair])
				total_areas.append(dat['total_area'])
			if len(obs) not in [0,2]: raise Exception('something has gone wrong')
			elif len(obs)==0: continue
			# average replicates
			counts = np.array(obs).mean(axis=0)
			nmols = nmols.get()
			total_area = np.mean(total_areas) #! total area is averaged between simulations for norming
			density = nmols[0]*nmols[1]/total_area
			post[pairname][sn_group] = dict(counts=counts,total_area=total_area,density=density,nmols=nmols)

def plot_radial_distributions(figname,mode='summary',**kwargs):
	"""Plot RDFs or CDFs of the RDFs."""
	# plot lipid-lipid RDFs on one panel
	reps_subset = kwargs.get('groups',None)
	if reps_subset: replicate_mapping_this = [(i,j) for i,j in replicate_mapping if i in reps_subset]
	else: replicate_mapping_this = replicate_mapping
	axes,fig = square_tiles(len(pairings),figsize=(12,12),hspace=0.4,wspace=0.4)
	for pnum,(pairname,pair) in enumerate(zip(pairings,pair_residues)):
		for sn_group,sns_this in replicate_mapping_this:
			if sn_group not in post[pairname]: continue
			counts,nmols,total_area,density = [post[pairname][sn_group][k]
				for k in ['counts','nmols','total_area','density']]
			ax = axes[pnum]
			#! show 90% of the maximum range
			xmax = 0.9*np.sqrt(total_area)
			valid = middles<xmax
			if mode=='cdf':
				valid = middles<xmax_cumulative
				vals = np.array([scipy.integrate.simps(counts[:i]/areas[:i]/density)
					for i in range(2,len(counts))])[valid]
				ax.plot(middles[valid],vals[valid],color=color_by_simulation(sns_this[0]),lw=2)
				ax.set_xlim((0,xmax_cumulative))
				ax.set_ylabel('$\int{g(r)}$')
			elif mode=='summary':
				valid = middles<xmax
				ax.plot(middles[valid],(counts/areas/density)[valid],
					color=color_by_simulation(sns_this[0]),lw=2)
				ax.axhline(1.0,lw=1,c='k')
				ax.set_xlim((0,xmax))
				ax.set_ylabel('$g(r)$')
				ax.axhline(1.0,lw=0.5,c='k')
			else: raise Exception
			ax.set_xlim((0,xmax_cumulative))
			ax.set_title('%s-%s'%tuple([work.vars['names']['short'].get(p,p) for p in pairname]))
			ax.tick_params(axis='y',which='both',left='off',right='off',labelleft='on')
			ax.tick_params(axis='x',which='both',top='off',bottom='off',labelbottom='on')
			ax.set_xlabel('$r\,(nm)$')
	legend = make_legend(axes[-1],keys=[i for i,j in replicate_mapping_this])
	picturesave('fig.lipid_rdfs.%s'%figname,directory=work.plotdir,meta={},extras=[legend])

@autoplot(plotrun)
def plot_distributions():
	figspec = {
		'summary':{'mode':'summary'},
		'summary.20':{'mode':'summary','groups':['pip2_20_no_chol','pip2_20']},
		'cumulative':{'mode':'cdf'},}
	for figname,spec in figspec.items(): plot_radial_distributions(figname=figname,**spec)

@autoplot(plotrun)
def plot_cumulative_compare():
	figname = 'cumulative_compare'
	axes,fig = square_tiles(len(pairings),figsize=(12,12),hspace=0.4,wspace=0.4)
	heights = {}
	for pnum,(pairname,pair) in enumerate(zip(pairings,pair_residues)):
		ax = axes[pnum]
		heights[pairname] = {}
		for snum,(sn_group,sns_this) in enumerate(replicate_mapping):
			if sn_group not in post[pairname]: continue
			counts,nmols,total_area,density = [post[pairname][sn_group][k]
				for k in ['counts','nmols','total_area','density']]
			# mimic the cdf routine
			valid = middles<xmax_cumulative
			vals = np.array([scipy.integrate.simps(counts[:i]/areas[:i]/density)
				for i in range(2,len(counts))])[valid]
			# the last value occurs at the xmax_cumulative
			#! check that this is precise?
			heights[pairname][sn_group] = vals[-1]
			ax.bar(snum,vals[-1],color=color_by_simulation(sns_this[0]),width=1.0)
		ax.tick_params(axis='y',which='both',left='off',right='off',labelleft='on')
		ax.tick_params(axis='x',which='both',top='off',bottom='off',labelbottom='on')
		ax.set_xticks([])
		ax.set_title('%s-%s'%tuple([work.vars['names']['short'].get(p,p) for p in pairname]))
	ymin = min([min(i.values()) for i in heights.values()])
	ymax = max([max(i.values()) for i in heights.values()])
	for ax in axes:
		ax.set_ylim((0.9*ymin,1.1*ymax))
	legend = make_legend(axes[-1])
	picturesave('fig.lipid_rdfs.%s'%figname,directory=work.plotdir,meta={},extras=[legend])

plotrun.routine = None
if __name__=='__main__': pass


from sklearn.tree import DecisionTreeRegressor
import pdb
import pandas as pd
import numpy as np
import matplotlib
import requests
import json
import statsmodels.api as sm
import re
import sklearn
from sklearn import linear_model, neighbors, ensemble, svm

from matplotlib import pyplot as plt

from sklearn import preprocessing, cross_validation, metrics, tree, decomposition, grid_search
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier, AdaBoostClassifier
from sklearn.linear_model import LogisticRegression, Perceptron, SGDClassifier, OrthogonalMatchingPursuit, RandomizedLogisticRegression
from sklearn.neighbors.nearest_centroid import NearestCentroid
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.cross_validation import train_test_split
from sklearn.grid_search import ParameterGrid
from sklearn.preprocessing import StandardScaler, normalize, RobustScaler
from scipy import optimize
import statsmodels.api as sm
import matplotlib.pyplot as plt
import multiprocessing as mp
from sklearn.cross_validation import train_test_split
from sklearn import metrics
from sklearn.cross_validation import cross_val_score
import os, timeit, sys, itertools, re, time, requests, random, functools, logging, csv, datetime
from sklearn import linear_model, neighbors, ensemble, svm, preprocessing
from sklearn import preprocessing, cross_validation, svm, metrics, tree, decomposition, svm
from sklearn.ensemble import BaggingClassifier, RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier, AdaBoostClassifier
from sklearn.linear_model import LogisticRegression, Perceptron, SGDClassifier, OrthogonalMatchingPursuit, RandomizedLogisticRegression
from sklearn.neighbors.nearest_centroid import NearestCentroid
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB
from sklearn.tree import DecisionTreeClassifier
from sklearn.grid_search import ParameterGrid
from sklearn.metrics import *
from sklearn.preprocessing import StandardScaler, RobustScaler, robust_scale, scale
from time import time

from handleData import *

'''
Model Dictionaries
'''

criteriaHeader = ['AUC', 'Accuracy', 'Function called', 'Precision at .05',
                  'Precision at .10', 'Precision at .2', 'Precision at .25', 'Precision at .5',
                  'Precision at .75','Precision at .85','Recall at .05','Recall at .10',
                  'Recall at .20','Recall at .25','Recall at .5','Recall at .75',
                  'Recall at .85','f1 at 0.05','f1 at 0.1','f1 at 0.2','f1 at 0.25',
                  'f1 at 0.5','f1 at 0.75','f1 at 0.85','test_time (sec)','train_time (sec)']

# no adaboost for now.
modelNames = ['LogisticRegression', 'KNeighborsClassifier', 'RandomForestClassifier', 'ExtraTreesClassifier',
              'AdaBoostClassifier', 'SVC', 'GaussianNB', 'DecisionTreeClassifier',
              'SGDClassifier']
n_estimMatrix = [5, 10, 25, 50, 100, 200]
depth = [10, 20, 50]
cpus = mp.cpu_count()
cores = cpus-1
modelLR = {'model': LogisticRegression, 'solver': ['liblinear'], 'C' : [.01, .1, .5, 1],#, 5, 10, 25],
          'class_weight': ['balanced', None], 'n_jobs' : [cores],
          'tol' : [1e-5, 1e-3, 1], 'penalty': ['l1', 'l2']} #tol also had 1e-7, 1e-4, 1e-1
modelKNN = {'model': neighbors.KNeighborsClassifier, 'weights': ['uniform', 'distance'], 'n_neighbors' : [100, 500, 1000],#2,5, 10, 50, 10000],
            'leaf_size': [60, 120]}#, 'n_jobs': [cpus/4]} #leaf size also had 15, 30
modelRF  = {'model': RandomForestClassifier, 'n_estimators': [25, 50, 100], 'criterion': ['gini', 'entropy'],
            'max_features': ['sqrt', 'log2'], 'max_depth': depth, 'min_samples_split': [20, 50], #min sample split also had 2, 5, 10
            'bootstrap': [True], 'n_jobs':[cores]} #bootstrap also had False

modelET  = {'model': ExtraTreesClassifier, 'n_estimators': [25, 50, 100], 'criterion': ['gini', 'entropy'],
            'max_features': ['sqrt', 'log2'], 'max_depth': depth,
            'bootstrap': [True, False], 'n_jobs':[cores]}

modelAB  = {'model': AdaBoostClassifier, 'algorithm': ['SAMME', 'SAMME.R'], 'n_estimators': [5, 10, 25, 50, 100]}#, 200]}
modelSVM = {'model': svm.SVC, 'C':[0.1,1], 'max_iter': [1000, 2000], 'probability': [True],
            'kernel': ['rbf', 'poly', 'sigmoid', 'linear']} #C was: [0.00001,0.0001,0.001,0.01,0.1,1,10]
modelNB  = {'model': GaussianNB}
modelDT  = {'model': DecisionTreeClassifier, 'criterion': ['gini', 'entropy'], 'max_depth': [10,20,50], #had 100, 1, 5
            'max_features': ['sqrt','log2'],'min_samples_split': [10, 20, 50]} #had a 2,5
modelSGD = {'model': SGDClassifier, 'loss': ['modified_huber'], 'penalty': ['l1', 'l2', 'elasticnet'],
            'n_jobs': [cores]}

modelList = [modelLR, modelKNN, modelRF, modelET,
             modelAB, modelSVM, modelNB, modelDT,
             modelSGD]

simple_modelSVC = {'model': svm.LinearSVC}
simple_modelLR = {'model': LogisticRegression}
simple_modelRF  = {'model': RandomForestClassifier}
simple_modelNB  = {'model': GaussianNB}
simple_modelDT  = {'model': DecisionTreeClassifier, 'max_depth': [50], 'max_features': ['sqrt'],
            'min_samples_split': [50]}
simple_modelDTR = {'model': DecisionTreeRegressor}

modelList = [simple_modelDT,simple_modelLR]

'''

main looping funcs

based on code from my group project repository

'''

def evaluate_model(y, pred_probs, train_times, test_times, accuracies, classifier, test_weights, sample_weights = False):
    """
    Takes in y values, the associated probabilities, times, accuracies, and the
    name of the classifier. Returns a dictionary with AUC, acc, etc. statistics
    """

    levels = ['Precision at .05', 'Precision at .10', 'Precision at .2', 'Precision at .25', 'Precision at .5', 'Precision at .75', 'Precision at .85']
    recalls = ['Recall at .05', 'Recall at .10', 'Recall at .20', 'Recall at .25', 'Recall at .5', 'Recall at .75', 'Recall at .85']
    amts= [.05, .1, .2, .25, .5, .75, .85]
    res = {}
    y_range = range(0, len(y))
    res['classifier'] = classifier
    for x in range(0, len(amts)):
        #print('check 1')
        thresh = amts[x]
        #pdb.set_trace()
        preds = [np.asarray([1 if j >= thresh else 0 for j in z]) for z in pred_probs]
        if sample_weights == True:
            try:
                prec = [metrics.precision_score(y[j], preds[j], sample_weight = test_weights[j]) for j in y_range]
                rec = [metrics.recall_score(y[j], preds[j], sample_weight = test_weights[j]) for j in y_range]
                f1_score = [metrics.f1_score(y[j],preds[j], sample_weight = test_weights[j]) for j in y_range]
            except:
                prec = [metrics.precision_score(y[j], preds[j]) for j in y_range]
                rec = [metrics.recall_score(y[j], preds[j]) for j in y_range]
                f1_score = [metrics.f1_score(y[j],preds[j]) for j in y_range]

        else:
            prec = [metrics.precision_score(y[j], preds[j]) for j in y_range]
            rec = [metrics.recall_score(y[j], preds[j]) for j in y_range]
            f1_score = [metrics.f1_score(y[j],preds[j]) for j in y_range]
        prec_std = np.std(prec)
        rec_std = np.std(rec)
        #print('check 2')
        f1_std = np.std(f1_score)

        prec_m = np.mean(prec)
        rec_m = np.mean(rec)
        f1_m = np.mean(f1_score)
        res[levels[x]] = str(prec_m) + ' (' + str(prec_std) + ')'
        res[recalls[x]] = str(rec_m) + ' (' + str(rec_std) + ')'
        res['f1 at ' + str(thresh)] = str(f1_m) + ' (' + str(f1_std) + ')'

    if sample_weights:
        try:
            auc = [metrics.roc_auc_score(y[j], pred_probs[j], sample_weight = test_weights[j]) for j in y_range]
        except:
            auc = [metrics.roc_auc_score(y[j], pred_probs[j]) for j in y_range]
    else:
        auc = [metrics.roc_auc_score(y[j], pred_probs[j]) for j in y_range]
    auc_std = np.std(auc)
    auc_m = np.mean(auc)
    train_m = np.mean(train_times)
    train_std = np.std(train_times)
    test_m = np.mean(test_times)
    test_std = np.std(test_times)
    acc_m = np.mean(accuracies)
    acc_std = np.std(accuracies)

    res['AUC'] = str(auc_m) + ' (' + str(auc_std) + ')'
    res['train_time (sec)'] = str(train_m) + ' (' + str(train_std) + ')'
    res['test_time (sec)'] = str(test_m) + ' (' + str(test_std) + ')'
    res['Accuracy'] = str(acc_m) + ' (' + str(acc_std) + ')' #mean_std_to_string(acc_m, acc_std)

    return res

'''
Return a result string as "mean (std)"
'''
def makeResultString(mean, std):
    return str(mean) + ' (' + str(std) + ')'

'''
Return a dictionary of a bunch of criteria. Namely, this returns a dictionary
with precision and recall at .05, .1, .2, .25, .5, .75, AUC, time to train, and
time to test.
'''
def getCriterionsNoProb(yTests, predProbs, train_times, test_times, accuracies, called, test_weights, sample_weights = False):
    levels = ['Precision at .05', 'Precision at .10', 'Precision at .2', 'Precision at .25', 'Precision at .5', 'Precision at .75', 'Precision at .85']
    recalls = ['Recall at .05', 'Recall at .10', 'Recall at .20', 'Recall at .25', 'Recall at .5', 'Recall at .75', 'Recall at .85']
    amts= [.05, .1, .2, .25, .5, .75, .85]
    tots = len(amts)
    res = {}
    critsLen = len(yTests)
    critsRange = range(0, critsLen)
    res['classifier'] = called
    for x in range(0, tots):
        thresh = amts[x]

        res[levels[x]] = ''
        res[recalls[x]] = ''
        res['f1 at ' + str(thresh)] = ''

    if sample_weights:
        try:
            auc = [metrics.roc_auc_score(yTests[j], predProbs[j], sample_weight = test_weights[j]) for j in critsRange]
        except:
            auc = [metrics.roc_auc_score(yTests[j], predProbs[j]) for j in critsRange]
    else:
        auc = [metrics.roc_auc_score(yTests[j], predProbs[j]) for j in critsRange]
    aucStd = np.std(auc)
    aucM = np.mean(auc)
    trainM = np.mean(train_times)
    trainStd = np.std(train_times)
    testM = np.mean(test_times)
    testStd = np.std(test_times)
    accM = np.mean(accuracies)
    accStd = np.std(accuracies)

    res['AUC'] = makeResultString(aucM, aucStd)
    res['train_time (sec)'] = makeResultString(trainM, trainStd)
    res['test_time (sec)'] = makeResultString(testM, testStd)
    res['Accuracy'] = makeResultString(accM, accStd)

    return res

def clf_loop_revolutions(X,y,k,clf_list,discr_var_names, bin_nums, s_weights,  sample_weights = False, col_name_frag= 'region'):
    results = []
    indx = 1
    indexer = 1

    cols = X.columns
    subsects = [c for c in cols if col_name_frag in c]
    yLen = len(y)
    catcher = {}

    '''
    need to put predicted probs back in same order as in data set
    then get statistics on that long list of predicted probs
    output that whole thing gancho
    Gonna have to make a dictionary with stuff
    '''

    for item in subsects:
        y_use = y[X[item] == 1]
        x_use = X[X[item] == 1]
        weight_use = s_weights[X[item] == 1]
        for clf_d in clf_list:
            print("\nIter: " + str(indexer) + "\n")
            param_grid = parameter_grid(clf_d)
            total = len(param_grid)
            res = [None]*total
            z = 0
            kf = cross_validation.KFold(len(y_use), k)

            for params in param_grid:
                print("Starting: "  + str(clf_d['model']))
                clf = clf_d['model'](**params)
                #try:
                train_times = [None]*k
                pred_probs = [None]*k
                test_times = [None]*k
                y_tests = [None]*k
                accs = [None]*k
                test_weights = [None]*k
                indx = 0
                noProb = False
                tmp_sample_weights = sample_weights
                for train, test in kf:
                    XTrain_init, XTest_init = x_use._slice(train, 0), x_use._slice(test, 0)
                    yTrain, yTest = y_use._slice(train, 0), y_use._slice(test, 0)
                    train_cross_weights = weights._slice(train, 0).as_matrix()
                    test_cross_weights = weights._slice(test, 0).as_matrix()
                    test_weights[indx] = test_cross_weights
                    y_tests[indx] = yTest

                    XTrain_discrete, train_bins = discretize(XTrain_init, discr_var_names, bin_nums)
                    XTrain = create_dummies(XTrain_discrete, discr_var_names)

                    XTest_discrete = discretize_given_bins(XTest_init, discr_var_names, train_bins)
                    XTest = create_dummies(XTest_discrete, discr_var_names)
                    '''
                    macro_var_names = readcsv('macro_var_names.csv')

                    #CHANGE THIS METHOD IF DESIRED
                    #===========================
                    method = StandardScaler()
                    #===========================

                    XTrain, XTest = macaroni(XTrain_update, XTest_update, macro_var_names, method)
                    '''
                    start = time.time()

                    if sample_weights == True:
                        try:
                            fitted = clf.fit(XTrain, yTrain, train_cross_weights)
                        except:
                            fitted = clf.fit(XTrain, yTrain)
                            tmp_sample_weights = False
                    else:
                        fitted = clf.fit(XTrain, yTrain)
                    #pdb.set_trace()
                    t_time = time.time() - start
                    train_times[indx] = t_time
                    start_test = time.time()
                    try:
                        pred_prob = fitted.predict_proba(XTest)[:,1]
                    except:
                        start_test = time.time()
                        pred_prob = fitted.predict(XTest)
                        noProb = True

                    test_time = time.time() - start_test
                    test_times[indx] = test_time
                    pred_probs[indx] = pred_prob

                    if sample_weights == True and tmp_sample_weights:
                        accs[indx] = fitted.score(XTest,yTest, test_cross_weights)
                    else:
                        accs[indx] = fitted.score(XTest,yTest)

                    indx += 1
                print('done training')
                model_name = str(clf)
                if not noProb:
                    evals = evaluate_model(y_tests, pred_probs, train_times, test_times, accs, model_name, s_weights, tmp_sample_weights)
                else:
                    evals = getCriterionsNoProb(y_tests, pred_probs, train_times, test_times, accs, model_name, s_weights, tmp_sample_weights)
                print('done evaluating')
                print(evals['AUC'])
                evals['Subsection'] = str(item)

                if sample_weights:
                    fitted = clf.fit(x_use, y_use, weight_use)
                else:
                    fitted = clf.fit(x_use, y_use)

                try:
                    full_preds = fitted.predict_proba(x_use)[:,1]
                except:
                    full_preds = fitted.predict(x_use)

                print('done getting pred_probs')
                res[z] = evals
                if model_name in catcher.keys():
                    catcher[model_name].update({str(item):(full_preds, noProb)})
                else:
                    catcher[model_name] = {str(item):(full_preds, noProb)}
                #except:
                #    print("Invalid params: " + str(params))
                #    continue
                z +=1
                s= str(z) + '/' + str(total)
                print(s)

            results += res
            indexer +=1

    fulls=[None]*len(catcher.keys())
    spot = 0
    for key in catcher.keys():
        fulls[spot] = getFullModel(catcher[key], X,y, s_weights,  sample_weights, col_name_frag, key)
        spot += 1
    results += fulls
    return [z for z in results if z != None]

'''
Need to finish below function
'''
def getFullModel(hTable, X, y, s_weights, sample_weights, col_name_frag, modelName):
    yLen = len(y)
    fullPred = [0]*yLen

    cols = X.columns
    subsects = [c for c in cols if col_name_frag in c]

    for s in subsects:
        tmpPred = hTable[s][0]
        i = 0
        z = 0
        for item in X[s]:
            if item != 0:
                fullPred[i] = tmpPred[z]
                z += 1
            i += 1

    l = list(y['q24'])
    sFP = [1 if v >= .5 else 0 for v in fullPred]
    accs = accuracy_score(l, sFP)
    if not hTable[s][1]:
        evals = evaluate_model([y], [fullPred], [0], [0], [accs], modelName + "_full", s_weights, sample_weights)
    else:
        evals = getCriterionsNoProb(y, [fullPred], [0], [0], [accs], modelName + "_full", s_weights, sample_weights)
    return evals

def clf_loop_reloaded(X,y,k,clf_list,discr_var_names, bin_nums, weights, sample_weights = False):
    results = []
    indx = 1

    for clf_d in clf_list:
        print("\nIter: " + str(indx) + "\n")
        param_grid = parameter_grid(clf_d)
        total = len(param_grid)
        res = [None]*total
        z = 0
        kf = cross_validation.KFold(len(y), k)

        for params in param_grid:
            print("Starting: "  + str(clf_d['model']))
            clf = clf_d['model'](**params)
            #try:
            train_times = [None]*k
            pred_probs = [None]*k
            test_times = [None]*k
            y_tests = [None]*k
            accs = [None]*k
            test_weights = [None]*k
            indx = 0
            noProb = False
            tmp_sample_weights = sample_weights
            for train, test in kf:
                XTrain_init, XTest_init = X._slice(train, 0), X._slice(test, 0)
                yTrain, yTest = y._slice(train, 0), y._slice(test, 0)
                train_cross_weights = weights._slice(train, 0).as_matrix()
                test_cross_weights = weights._slice(test, 0).as_matrix()
                test_weights[indx] = test_cross_weights
                y_tests[indx] = yTest

                XTrain_discrete, train_bins = discretize(XTrain_init, discr_var_names, bin_nums)
                XTrain = create_dummies(XTrain_discrete, discr_var_names)

                XTest_discrete = discretize_given_bins(XTest_init, discr_var_names, train_bins)
                XTest = create_dummies(XTest_discrete, discr_var_names)

                '''
                macro_var_names = readcsv('macro_var_names.csv')

                #CHANGE THIS METHOD IF DESIRED
                #===========================
                method = StandardScaler()
                #===========================

                XTrain, XTest = macaroni(XTrain_update, XTest_update, macro_var_names, method)
                '''

                start = time.time()
                if sample_weights == True:
                    try:
                        fitted = clf.fit(XTrain, yTrain, train_cross_weights)
                    except:
                        fitted = clf.fit(XTrain, yTrain)
                        tmp_sample_weights = False
                else:
                    fitted = clf.fit(XTrain, yTrain)
                #pdb.set_trace()
                t_time = time.time() - start
                train_times[indx] = t_time
                start_test = time.time()
                try:
                    pred_prob = fitted.predict_proba(XTest)[:,1]
                except:
                    start_test = time.time()
                    pred_prob = fitted.predict(XTest)
                    noProb = True

                test_time = time.time() - start_test
                test_times[indx] = test_time
                pred_probs[indx] = pred_prob
                if sample_weights == True and tmp_sample_weights:
                    accs[indx] = fitted.score(XTest,yTest, test_cross_weights)
                else:
                    accs[indx] = fitted.score(XTest,yTest)
                indx += 1
            print('done training')
            if not noProb:
                evals = evaluate_model(y_tests, pred_probs, train_times, test_times, accs, str(clf),test_weights, tmp_sample_weights)
            else:
                evals = getCriterionsNoProb(y_tests, pred_probs, train_times, test_times, accs, str(clf),test_weights, tmp_sample_weights)
            print('done evaluating')
            print(evals['AUC'])
            res[z] = evals
            #except:
            #    print("Invalid params: " + str(params))
            #    continue
            z +=1
            s= str(z) + '/' + str(total)
            print(s)

        results += res
        indx +=1

    return [z for z in results if z != None]

'''

support funcs

'''

def parameter_grid(old_d):
    '''
    Similar to ParameterGrid() from sklearn
    '''
    result = []
    d = old_d.copy()
    del d['model']
    #d = remove_key(old_d, 'model')
    keys  = d.keys()
    l = [d[x] for x in keys]
    combos = list(itertools.product(*l))
    num_combos = len(combos)
    result = [0] * num_combos

    for i in range(0, num_combos):
        result[i] = dict(zip(keys, combos[i]))

    return result

'''

output functions

'''

def extractPredsItem(listy, keyName):
    res = [None]*len(listy)

    length  = len(listy)
    for j in range(0, length):
        res[j] = listy[j][1][keyName]
    return [z for z in res if z != None]

def write_results_to_file(file_name, d, has_pred_probs = False, pred_file_name = 'Prediction_probs.csv'):
    if has_pred_probs:
        header = [x for x in d[0][0].keys()]
        header.sort()
        fin = format_data(header, d[0])
        fin.insert(0, header)

        header2 = extractPredsItem(d, 'Model-subsect')
        fin2 = extractPredsItem(d, 'Prob_preds')
        fin2.insert(0, header2)

        try:
            with open(file_name, "w") as file_out:
                writer = csv.writer(file_out)
                for f in fin:
                    writer.writerow(f)
                file_out.close()
        except:
            print('writing failed')

        try:
            with open(pred_file_name, "w") as fout:
                writer = csv.writer(fout)
                for f in fin2:
                    writer.writerow(f)
                fout.close()
        except:
            print('Pred_probs writing failed')
        return

    header = [x for x in d[0].keys()] # header of eval criteria
    header.sort()
    fin = format_data(header, d)
    fin.insert(0, header)
    try:
        with open(file_name, "w") as file_out:
            writer = csv.writer(file_out)
            for f in fin:
                writer.writerow(f)
            file_out.close()
    except:
        print('writing failed')
    return

def format_data(header, d):
    len_d = len(d)
    formatted = [[]] * len_d
    len_header = len(header)

    indxForm = 0
    indx = 0
    for x in d:
        tmp = [None] * len_header
        for j in header:
            tmp[indx] = x[j]
            indx += 1
        indx = 0
        formatted[indxForm] = tmp
        indxForm += 1
    return formatted

def clean_results(data,target_cols):
    """
    strips standard deviations from csv-derived DataFrame
    and replaces strings with floats
    """
    str_to_num = lambda x: float(x[0:x.index('(')]) if type(x) is str else x
    for col in target_cols:
        data[col] = data[col].apply(str_to_num)
        data[col] = data[col].fillna(0)
    return data

def best_given_metric(data,metric,n=5,ascending_toggle=False):
    """
    returns the n best classifiers by a given metric
    """
    assert metric in criteriaHeader
    sorted_data = data.sort_values(metric,ascending=ascending_toggle)
    return sorted_data.iloc[0:n,:]

def best_by_each_metric(data):
    """
    returns the best classifiers by each metric
    """
    indices = []
    metric_list = []
    criteria = criteriaHeader.copy()
    if 'Function called' in criteria:
        criteria.remove('Function called')
    for metric in criteria:
        if 'sec' in metric:
            best = best_given_metric(data,metric,n=1,ascending_toggle=True).index[0]
        else:
            best = best_given_metric(data,metric,n=1,ascending_toggle=False).index[0]
        indices.append(best)
        metric_list.append(metric)
    output = data.iloc[indices,:]
    output['best metric'] = metric_list
    cols = list(sorted(output.columns))
    cols.remove('classifier')
    cols.remove('best metric')
    cols = ['classifier','best metric'] + cols
    output = output.reindex_axis(cols, axis=1)
    output = output.reindex(list(range(len(output))))
    return output

def compare_clf_across_metric(data,metric):
    """
    For the given metric, finds the parameterization of each clf that performed the best
    """
    indices = []
    assert metric in criteriaHeader
    if 'sec' in metric:
        ascending = True
    else:
        ascending = False

    tester = lambda x,y: y in x['classifier']
    for clf in modelNames:
        clf_subset = data[data.apply(lambda x: tester(x,clf),1)]
        if len(clf_subset)>0:
            best = best_given_metric(clf_subset,metric,1,ascending).index[0]
            indices.append(best)
    print(indices)
    output = data.iloc[indices,:]
    cols = list(sorted(output.columns))
    cols.remove('classifier')
    cols = ['classifier'] + cols
    output = output.reindex_axis(cols, axis=1)
    output = output.reindex(list(range(len(output))))
    return output

def plot_precision_recall_from_results(data,target_rows):
    """"
    Uses output from clean_results, compare_clf_across_metric, best_by_each_metric
    and plots precision recall curves for each row in target_rows
    """

    precision_cols = sorted([x for x in data.columns if 'Precision' in x])
    recall_cols = sorted([x for x in data.columns if 'Recall' in x])
    grab_thresh = lambda x: float(x[x.index('.'):])
    thresholds = [grab_thresh(x) for x in precision_cols]
    fig,ax = plt.subplots(1,1)

    for r in target_rows:
        x = [data.ix[r,col] for col in precision_cols]
        y = [data.ix[r,col] for col in recall_cols]
        ax.plot(x,y,label=data.ix[r,'classifier'])
    ax.legend(loc='bottom left')
    plt.show()
    return


# -*- coding: utf-8 -*-
from __future__ import division
from __future__ import print_function

import numpy as np

import torch
import torch.nn.functional as F

'''
Average:
An utterance representation can be obtained by averaging the embeddings of all the words in that utterance, of which the cosine similarity gives the Average metric
'''

def get_top_k_fact_average_batch(encoder_embedding, fact_embedding, encoder_embedding_size,
                                fact_embedding_size, encoder_inputs, fact_inputs,
                                batch_size=1, top_k=20, device=None):

    encoder_inputs_embedded = encoder_embedding(encoder_inputs)
    encoder_inputs_embedded_mean = encoder_inputs_embedded.transpose(0, 1).mean(dim=1)  # [batch_size, embedding_size]

    new_fact_inputs = torch.zeros((batch_size, top_k, fact_embedding_size), device=device)
    for bi in range(batch_size):
        cur_fact_input = fact_inputs[bi]
        # cur_fact_input: [total_n, len()]
        cur_fact_input_embedded_mean = torch.zeros(
            (len(cur_fact_input), fact_embedding_size), device=device)
        for fi, cur_part_fact in enumerate(cur_fact_input):
            cur_part_fact = torch.LongTensor(cur_part_fact, device=device)
            cur_part_fact_embedded = fact_embedding(cur_part_fact.view(1, -1))
            cur_part_fact_embedded_mean = cur_part_fact_embedded.mean(dim=1)
            cur_fact_input_embedded_mean[fi] = cur_part_fact_embedded_mean.view(-1)

        # get top_k
        cur_encoder_input_embedded_mean = encoder_inputs_embedded_mean[bi]
        cosine_scores = F.cosine_similarity(
            cur_encoder_input_embedded_mean, cur_fact_input_embedded_mean)

        # sort
        sorted_scores, sorted_indices = cosine_scores.sort(
            dim=0, descending=True)
        top_k_indices = sorted_indices[:top_k]
        # [top_k, embedding_size]
        top_k_fact_embedded = cur_fact_input_embedded_mean[top_k_indices]
        new_fact_inputs[bi] = top_k_fact_embedded

    return new_fact_inputs

'''without batch'''

def get_top_k_fact_average(encoder_embedding, fact_embedding, encoder_embedding_size,
                           fact_embedding_size, conversation_ids, facts_ids, facts_weight,
                           top_k=20, device=None):
    """
    Args:
        - conversation_ids: [len]
        - facts_ids: total_num, len
    """
    with torch.no_grad():
        encoder_input = torch.tensor(conversation_ids, dtype=torch.long, device=device)
        encoder_input_embedded = encoder_embedding(encoder_input, is_dropout=False)
        encoder_input_embedded_mean = encoder_input_embedded.mean(dim=0).unsqueeze(0)  # [1, embedding_size]

        facts_embedded_mean = torch.zeros((len(facts_ids), fact_embedding_size), device=device)
        for fi, fact_ids in enumerate(facts_ids):
            fact_input = torch.tensor(fact_ids, dtype=torch.long, device=device)
            fact_input_embedded = fact_embedding(fact_input, is_dropout=False)
            fact_input_embedded_mean = fact_input_embedded.mean(dim=0)  # [embedding_size]
            facts_embedded_mean[fi] = fact_input_embedded_mean

        # get top_k
        cosine_scores = F.cosine_similarity(facts_embedded_mean, encoder_input_embedded_mean)  # [len(facts_ids)]

        cosine_scores = cosine_scores * facts_weight

        # * tag_weight

        # sort
        _, sorted_indices = cosine_scores.sort(dim=0, descending=True)

        top_k_indices = sorted_indices[:top_k]
        # [top_k, embedding_size]
        #  top_k_facts_embedded_mean = facts_embedded_mean[top_k_indices]

        top_k_facts_embedded_mean = torch.zeros((top_k, fact_embedding_size), device=device)
        for i in top_k_indices:
            top_k_facts_embedded_mean[i] = facts_embedded_mean[i]

        del facts_embedded_mean
        del encoder_input_embedded_mean
        del encoder_input_embedded
        del encoder_input
        del cosine_scores

    return top_k_facts_embedded_mean.detach().cpu(), top_k_indices.detach().cpu()

'''
Extreme:
Achieve an utterance representation by taking the
largest extreme values among the embedding vectors of all the words it contains
'''

def get_extreme_embedding_score(vocab, gensim_model, input_str, candidate_replies,
                                stop_word_obj, lower=None, normal=False):
    query_words = stop_word_obj.remove_words(input_str.strip().replace('\t', ' '))  # to lower if lower:
    query_words = [word.lower() for word in query_words]

    query_words_embedding = []
    for word in query_words:
        try:
            word_embedding = gensim_model.wv[word]
        except KeyError:
            word_embedding = gensim_model.wv[vocab.unk]
        query_words_embedding.append(word_embedding)

    extreme_vector_query = np.array(query_words_embedding).max(axis=0)

    extreme_matrix_candidate = []
    for candidate_reply in candidate_replies:
        candidate_words = stop_word_obj.remove_words(
            candidate_reply.strip().replace('\t', ' '))

        # to lower
        if lower:
            candidate_words = [word.lower() for word in candidate_words]

        candidate_words_embedding = []
        for word in candidate_words:
            try:
                word_embedding = gensim_model.wv[word]
            except KeyError:
                word_embedding = gensim_model.wv[vocab.unk]

            candidate_words_embedding.append(word_embedding)

        extreme_vector_candidate = np.array(
            candidate_words_embedding).max(axis=0)
        extreme_matrix_candidate.append(extreme_vector_candidate)

    extreme_matrix_candidate = np.array(extreme_matrix_candidate)

    score_vector = gensim_model.cosine_similarities(
        extreme_vector_query, extreme_matrix_candidate)

    # normalization
    if normal:
        score_vector = score_normalization(score_vector)

    return score_vector

'''
Greedy:
Greedily match words in two given utterances based on the cosine similarities of their embeddings,
and to average the obtained scores
'''

def get_greedy_embedding_score(vocab, gensim_model, input_str, candidate_replies, stop_word_obj, lower=None,
                               normal=False):
    query_words = stop_word_obj.remove_words(
        input_str.strip().replace('\t', ' '))

    # to lower
    if lower:
        query_words = [word.lower() for word in query_words]

    score_vector = []
    for candidate_reply in candidate_replies:
        candidate_words = stop_word_obj.remove_words(
            candidate_reply.strip().replace('\t', ' '))

        # to lower
        if lower:
            candidate_words = [word.lower() for word in candidate_words]

        max_scores = []
        for query_word in query_words:
            max_score = 0.0
            for candidate_word in candidate_words:
                try:
                    score = gensim_model.wv.similarity(
                        query_word, candidate_word)
                except KeyError:
                    score = -1.0

                max_score = max(score, max_score)

            max_scores.append(max_score)

        score_vector.append(np.mean(max_scores))

    # normalization
    if normal:
        score_vector = score_normalization(score_vector)

    return score_vector

'''
Optimal Matching
TODO
'''

'''
Word Mover's Distance
'''

def get_wmd_embedding_score(gensim_model, input_str, candidate_replies, stop_word_obj, lower=None, normal=False):
    distance_vector = np.random.rand(len(candidate_replies))

    query_words = stop_word_obj.remove_words(
        input_str.strip().replace('\t', ' '))
    # to lower
    if lower:
        query_words = [word.lower() for word in query_words]

    for idx, candidate_reply in enumerate(candidate_replies):
        candidate_words = stop_word_obj.remove_words(
            candidate_reply.strip().replace('\t', ' '))
        # to lower
        if lower:
            candidate_words = [word.lower() for word in candidate_words]

        distance_vector[idx] = gensim_model.wmdistance(
            query_words, candidate_words)

    # distance to score
    score_vector = np.max(distance_vector) - distance_vector

    # normalization
    if normal:
        score_vector = score_normalization(score_vector)

    return score_vector

'''
Max-Min normalization.
'''

def score_normalization(score_vector):
    max_score = np.max(score_vector)
    min_score = np.min(score_vector)
    score_vector = np.divide((score_vector - min_score),
                             (max_score - min_score) * 1.0)
    return score_vector


# Copyright Contributors to the Pyro project.
# SPDX-License-Identifier: Apache-2.0

import pickle

import numpy as np
from numpy.testing import assert_allclose
import pytest

from jax import random
import jax.numpy as jnp
from jax.tree_util import tree_all, tree_map

import numpyro
import numpyro.distributions as dist
from numpyro.infer import (
    HMC,
    HMCECS,
    MCMC,
    NUTS,
    SA,
    SVI,
    BarkerMH,
    DiscreteHMCGibbs,
    MixedHMC,
    Predictive,
)
from numpyro.infer.autoguide import AutoDelta, AutoDiagonalNormal, AutoNormal

def normal_model():
    numpyro.sample("x", dist.Normal(0, 1))

def bernoulli_model():
    numpyro.sample("x", dist.Bernoulli(0.5))

def logistic_regression():
    data = jnp.arange(10)
    x = numpyro.sample("x", dist.Normal(0, 1))
    with numpyro.plate("N", 10, subsample_size=2):
        batch = numpyro.subsample(data, 0)
        numpyro.sample("obs", dist.Bernoulli(logits=x), obs=batch)

@pytest.mark.parametrize("kernel", [BarkerMH, HMC, NUTS, SA])
def test_pickle_hmc(kernel):
    mcmc = MCMC(kernel(normal_model), num_warmup=10, num_samples=10)
    mcmc.run(random.PRNGKey(0))
    pickled_mcmc = pickle.loads(pickle.dumps(mcmc))
    tree_all(tree_map(assert_allclose, mcmc.get_samples(), pickled_mcmc.get_samples()))

@pytest.mark.parametrize("kernel", [DiscreteHMCGibbs, MixedHMC])
def test_pickle_discrete_hmc(kernel):
    mcmc = MCMC(kernel(HMC(bernoulli_model)), num_warmup=10, num_samples=10)
    mcmc.run(random.PRNGKey(0))
    pickled_mcmc = pickle.loads(pickle.dumps(mcmc))
    tree_all(tree_map(assert_allclose, mcmc.get_samples(), pickled_mcmc.get_samples()))

def test_pickle_hmcecs():
    mcmc = MCMC(HMCECS(NUTS(logistic_regression)), num_warmup=10, num_samples=10)
    mcmc.run(random.PRNGKey(0))
    pickled_mcmc = pickle.loads(pickle.dumps(mcmc))
    tree_all(tree_map(assert_allclose, mcmc.get_samples(), pickled_mcmc.get_samples()))

def poisson_regression(x, N):
    rate = numpyro.sample("param", dist.Gamma(1.0, 1.0))
    batch_size = len(x) if x is not None else None
    with numpyro.plate("batch", N, batch_size):
        numpyro.sample("x", dist.Poisson(rate), obs=x)

@pytest.mark.parametrize("guide_class", [AutoDelta, AutoDiagonalNormal, AutoNormal])
def test_pickle_autoguide(guide_class):
    x = np.random.poisson(1.0, size=(100,))

    guide = guide_class(poisson_regression)
    optim = numpyro.optim.Adam(1e-2)
    svi = SVI(poisson_regression, guide, optim, numpyro.infer.Trace_ELBO())
    svi_result = svi.run(random.PRNGKey(1), 3, x, len(x))
    pickled_guide = pickle.loads(pickle.dumps(guide))

    predictive = Predictive(
        poisson_regression,
        guide=pickled_guide,
        params=svi_result.params,
        num_samples=1,
        return_sites=["param", "x"],
    )
    samples = predictive(random.PRNGKey(1), None, 1)
    assert set(samples.keys()) == {"param", "x"}


import pandas as pd
from datetime import datetime
from django.utils import timezone
import logging
from source.models import Source
from package.models import Package, PackageHistory
logger = logging.getLogger(__name__)

def get_path(mount, pkg_hash, gsb_verdict):
    """
    if path is NUll or None, Then path hash will be checked based on gsb status whether it is in benign or malicious
    folder. The path will be stored in the db
    """
    if mount[-1] != '/':
        mount += '/'
    if gsb_verdict == 1:
        path = mount+"malicious/"+pkg_hash
    else:
        path = mount+"benign/"+pkg_hash
    return path

def reset_uncategorized_verdict():
    """Reset the uncategorized to back"""
    print("**** Verdict Reset Cronjob ****")
    pkg_history = PackageHistory.objects.filter(verdict=6)
    for pkg_his in pkg_history:
        pkg = Package.objects.get(pk=pkg_his.package_id.id)
        pkg.status = 0
        pkg.save()
        pkg_his.delete()
        print("verdict reset with package hash: {}".format(pkg_his.package_id.hash))
    return True

def reset_packages_status():
    """
    Reset packages with status -1 to be available next. The method will run in cron job for every 2 hours
    :return:
    """

    def get_standard_path(df):
        current_time = datetime.datetime.now()
        updated_at = df["updated_at"]
        time_diff = current_time - updated_at
        time_diff = divmod(time_diff.days * 86400 + time_diff.seconds, 60)
        if time_diff[1] >= 10:
            Package.objects.filter(id=df["id"]).update(status=0)
        return df

    try:
        pkgs = pd.DataFrame(Package.objects.filter(status=-1).values())
        pkgs.apply(lambda row: get_standard_path(row), axis=1)
        print("Package reset cron run successfully")
    except Exception as e:
        print("Package reset cron abort with exception:\t {}".format(e))