import cv2
import numpy as np
import math
import json
import os

#from config import cfg
from utils.box_overlaps import *
from config import cfg

def process_pointcloud(point_cloud, cfg):
    # Input:
    #   (N, 4)
    # Output:
    #   voxel_dict
    scene_size = np.array(cfg.SCENE_SIZE, dtype=np.float32)
    voxel_size = np.array(cfg.VOXEL_SIZE, dtype=np.float32)
    grid_size = np.array(cfg.GRID_SIZE, dtype=np.int64)
    lidar_coord = np.array(cfg.LIDAR_COORD, dtype=np.float32)
    max_point_number = cfg.MAX_POINT_NUMBER

    if cfg.DETECT_OBJECT != "Car":
        np.random.shuffle(point_cloud)

    shifted_coord = point_cloud[:, :3] + lidar_coord
    # reverse the point cloud coordinate (X, Y, Z) -> (Z, Y, X)
    voxel_index = np.floor(
        shifted_coord[:, ::-1] / voxel_size).astype(np.int)

    bound_x = np.logical_and(
        voxel_index[:, 2] >= 0, voxel_index[:, 2] < grid_size[2])
    bound_y = np.logical_and(
        voxel_index[:, 1] >= 0, voxel_index[:, 1] < grid_size[1])
    bound_z = np.logical_and(
        voxel_index[:, 0] >= 0, voxel_index[:, 0] < grid_size[0])

    bound_box = np.logical_and(np.logical_and(bound_x, bound_y), bound_z)

    point_cloud = point_cloud[bound_box]
    voxel_index = voxel_index[bound_box]

    # [K, 3] coordinate buffer as described in the paper
    coordinate_buffer = np.unique(voxel_index, axis=0)

    K = len(coordinate_buffer)
    T = max_point_number

    # [K, 1] store number of points in each voxel grid
    number_buffer = np.zeros(shape=(K), dtype=np.int64)

    # [K, T, 7] feature buffer as described in the paper
    feature_buffer = np.zeros(shape=(K, T, 7), dtype=np.float32)

    # build a reverse index for coordinate buffer
    index_buffer = {}
    for i in range(K):
        index_buffer[tuple(coordinate_buffer[i])] = i

    for voxel, point in zip(voxel_index, point_cloud):
        index = index_buffer[tuple(voxel)]
        number = number_buffer[index]
        if number < T:
            feature_buffer[index, number, :4] = point
            number_buffer[index] += 1

    feature_buffer[:, :, -3:] = feature_buffer[:, :, :3] - \
        feature_buffer[:, :, :3].sum(axis=1, keepdims=True)/number_buffer.reshape(K, 1, 1)

    voxel_dict = {'feature_buffer': feature_buffer,
                  'coordinate_buffer': coordinate_buffer,
                  'number_buffer': number_buffer}
    return voxel_dict

# transformation matrix converts from sensorA->sensorB to sensorB->sensorA
def inv_trans(T):
    rotation = np.linalg.inv(T[0:3, 0:3])  # rotation matrix

    translation = T[0:3, 3]
    translation = -1 * np.dot(rotation, translation.T)
    translation = np.reshape(translation, (3, 1))
    Q = np.hstack((rotation, translation))

    return Q

def quat_to_rotation(quat):
    # input: (4, 1)
    # output: (4, 4)
    m = np.sum(np.multiply(quat, quat))
    q = quat.copy()
    q = np.array(q)
    n = np.dot(q, q)
    if n < np.finfo(q.dtype).eps:
        rot_matrix = np.identity(4)
        return rot_matrix
    q = q * np.sqrt(2.0 / n)
    q = np.outer(q, q)
    rot_matrix = np.array(
        [[1.0 - q[2, 2] - q[3, 3], q[1, 2] + q[3, 0], q[1, 3] - q[2, 0]],
         [q[1, 2] - q[3, 0], 1.0 - q[1, 1] - q[3, 3], q[2, 3] + q[1, 0]],
         [q[1, 3] + q[2, 0], q[2, 3] - q[1, 0], 1.0 - q[1, 1] - q[2, 2]]],
        dtype=q.dtype)
    rot_matrix = np.transpose(rot_matrix)
    # # test if it is truly a rotation matrix
    # d = np.linalg.det(rotation)
    # t = np.transpose(rotation)
    # o = np.dot(rotation, t)
    return rot_matrix

def qaut_to_angle(quat):
    w=quat[0]
    x=quat[1]
    y=quat[2]
    z=quat[3]

    roll = math.atan2(2*(w*x+y*z),1-2*(x*x+y*y))#the rol is the yaw angle!
    pitch = math.asin(2*(w*y-x*z))
    yaw = math.atan2(2*(w*z+x*y),1-2*(z*z+y*y))
    return roll, pitch, yaw

def angle_to_quat(roll, pitch, yaw):
    cy = math.cos(yaw * 0.5)
    sy = math.sin(yaw * 0.5)
    cp = math.cos(pitch * 0.5)
    sp = math.sin(pitch * 0.5)
    cr = math.cos(roll * 0.5)
    sr = math.sin(roll * 0.5)

    q = np.zeros(4)
    q[0] = cr * cp * cy + sr * sp * sy
    q[1] = sr * cp * cy - cr * sp * sy
    q[2] = cr * sp * cy + sr * cp * sy
    q[3] = cr * cp * sy - sr * sp * cy

    return q

#-- util function to load calib matrices
def load_calib(calib_dir):
    # output: 3 matrix
    with open(calib_dir, mode='r') as f:
        data = json.load(f)
    T_fromLidar = np.array(data['sensors'][1]['calib_data']['T_to_ref_COS'])
    T_fromCamera = np.array(data['sensors'][2]['calib_data']['T_to_ref_COS'])
    K = np.array(data['sensors'][2]['calib_data']['K'])

    T_toLidar = inv_trans(T_fromLidar)
    T_toCamera = inv_trans(T_fromCamera)
    return T_toLidar, T_toCamera, K

def get_class_id(classname):
    classes = {'Bus': 0, 'Car':1, 'Cyclist': 2, 'Motorcyclist': 3, 'Person': 4, 'Trailer':5, 'Truck':6, 'Towed Object': 5, 'Other Vehicle': 5}
    return classes[classname]

def load_label(label_dir):
    # output: [N,11]
    with open(label_dir, mode='r') as f:
        data = json.load(f)
    objects_info = data['objects']
    label = np.empty((len(objects_info), 11))

    for i, p in enumerate(objects_info):
        label[i,:] = np.array([p['center3d'][0], p['center3d'][1], p['center3d'][2],
                              p['dimension3d'][0], p['dimension3d'][1], p['dimension3d'][2],
                              p['orientation_quat'][0], p['orientation_quat'][1], p['orientation_quat'][2], p['orientation_quat'][3],
                              get_class_id(p['classname'])])

    return label

def lidar_to_bird_view(x, y, factor=1):
    # using the cfg.INPUT_XXX
    a = (x - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor
    b = (y - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor
    a = np.clip(a, a_max=(cfg.X_MAX - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor, a_min=0)
    b = np.clip(b, a_max=(cfg.Y_MAX - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor, a_min=0)
    return a, b

def batch_lidar_to_bird_view(points, factor=1):
    # Input:
    #   points (N, 2)
    # Outputs:
    #   points (N, 2)
    # using the cfg.INPUT_XXX
    a = (points[:, 0] - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor
    b = (points[:, 1] - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor
    a = np.clip(a, a_max=(cfg.X_MAX - cfg.X_MIN) / cfg.VOXEL_X_SIZE * factor, a_min=0)
    b = np.clip(b, a_max=(cfg.Y_MAX - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor, a_min=0)
    return np.concatenate([a[:, np.newaxis], b[:, np.newaxis]], axis=-1)

def angle_in_limit(angle):
    # To limit the angle in -pi/2 - pi/2
    limit_degree = 5
    while angle >= np.pi / 2:
        angle -= np.pi
    while angle < -np.pi / 2:
        angle += np.pi
    if abs(angle + np.pi / 2) < limit_degree / 180 * np.pi:
        angle = np.pi / 2
    return angle

def camera_to_lidar(x, y, z, T_VELO_2_CAM=None, R_RECT_0=None):
    if type(T_VELO_2_CAM) == type(None):
        T_VELO_2_CAM = np.array(cfg.MATRIX_T_VELO_2_CAM)

    if type(R_RECT_0) == type(None):
        R_RECT_0 = np.array(cfg.MATRIX_R_RECT_0)

    p = np.array([x, y, z, 1])
    p = np.matmul(np.linalg.inv(R_RECT_0), p)
    p = np.matmul(np.linalg.inv(T_VELO_2_CAM), p)
    p = p[0:3]
    return tuple(p)

def lidar_to_camera(x, y, z, T_VELO_2_CAM=None, R_RECT_0=None):
    if type(T_VELO_2_CAM) == type(None):
        T_VELO_2_CAM = np.array(cfg.MATRIX_T_VELO_2_CAM)

    if type(R_RECT_0) == type(None):
        R_RECT_0 = np.array(cfg.MATRIX_R_RECT_0)

    p = np.array([x, y, z, 1])
    p = np.matmul(T_VELO_2_CAM, p)
    #p = np.matmul(R_RECT_0, p)
    #p = p[0:3]
    return tuple(p)

def camera_to_lidar_point(points, T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 3) -> (N, 3)
    N = points.shape[0]
    points = np.hstack([points, np.ones((N, 1))]).T  # (N,4) -> (4,N)

    if type(T_VELO_2_CAM) == type(None):
        T_VELO_2_CAM = np.array(cfg.MATRIX_T_VELO_2_CAM)

    if type(R_RECT_0) == type(None):
        R_RECT_0 = np.array(cfg.MATRIX_R_RECT_0)

    points = np.matmul(np.linalg.inv(R_RECT_0), points)
    points = np.matmul(np.linalg.inv(T_VELO_2_CAM), points).T  # (4, N) -> (N, 4)
    points = points[:, 0:3]
    return points.reshape(-1, 3)

def lidar_to_camera_point(points, T_VELO_2_CAM=None):
    # (N, 3) -> (3, N)
    N = points.shape[0]
    points = np.hstack([points, np.ones((N, 1))]).T

    points = np.matmul(T_VELO_2_CAM, points)
    #points = points[:, 0:3]
    return points

def camera_to_lidar_box(boxes, T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 7) -> (N, 7) x,y,z,h,w,l,r
    ret = []
    for box in boxes:
        x, y, z, h, w, l, ry = box
        (x, y, z), h, w, l, rz = camera_to_lidar(
            x, y, z, T_VELO_2_CAM, R_RECT_0), h, w, l, -ry - np.pi / 2
        rz = angle_in_limit(rz)
        ret.append([x, y, z, h, w, l, rz])
    return np.array(ret).reshape(-1, 7)

def lidar_to_camera_box(boxes, T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 7) -> (N, 7) x,y,z,h,w,l,r
    ret = []
    for box in boxes:
        x, y, z, h, w, l, rz = box
        (x, y, z), h, w, l, ry = lidar_to_camera(
            x, y, z, T_VELO_2_CAM, R_RECT_0), h, w, l, -rz - np.pi / 2
        ry = angle_in_limit(ry)
        ret.append([x, y, z, h, w, l, ry])
    return np.array(ret).reshape(-1, 7)

def center_to_corner_box2d(boxes_center, coordinate='lidar', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 5) -> (N, 4, 2)
    N = boxes_center.shape[0]
    boxes3d_center = np.zeros((N, 7))
    boxes3d_center[:, [0, 1, 4, 5, 6]] = boxes_center
    boxes3d_corner = center_to_corner_box3d(boxes3d_center, coordinate=coordinate, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)

    return boxes3d_corner[:, 0:4, 0:2]

def center_to_corner_box3d(boxes_center, coordinate='lidar', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 10) -> (N, 8, 3)
    # (N, 7) -> (N, 8, 3)

    N = boxes_center.shape[0]
    ret = np.zeros((N, 8, 3), dtype=np.float32)

    if coordinate == 'camera':
        boxes_center = camera_to_lidar_box(boxes_center, T_VELO_2_CAM, R_RECT_0)

    for i in range(N):
        box = boxes_center[i]
        translation = box[0:3]
        size = box[3:6]

        w, l, h = size[0], size[1], size[2]
        trackletBox = np.array([
            [w / 2, -w / 2, -w / 2, w / 2, w / 2, -w / 2, -w / 2, w / 2],\
            [l / 2, l / 2, -l / 2, -l / 2, l / 2, l / 2, -l / 2, -l / 2],\
            [h / 2, h / 2, h / 2, h / 2, -h / 2, -h / 2, -h / 2, -h / 2]])
        # rotate and translate 3d bounding box
        if box.shape[0] == 10:
            quaternion = box[6:10]
            rotMat = quat_to_rotation(quaternion)
        else:
            yaw = box[6]
            rotMat = np.array([
              [np.cos(yaw), -np.sin(yaw), 0.0],
              [np.sin(yaw), np.cos(yaw), 0.0],
              [0.0, 0.0, 1.0]])

        cornerPosInVelo = np.dot(rotMat, trackletBox) + np.tile(translation, (8, 1)).T
        box3d = cornerPosInVelo.transpose()
        ret[i] = box3d

    #print(f'bounding box:{ret[0]}')
    # for idx in range(len(ret)):
    #     ret[idx] = lidar_to_camera_point(ret[idx], T_VELO_2_CAM)

    return ret

def corner_to_center_box2d(boxes_corner, coordinate='lidar', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 4, 2) -> (N, 5)  x,y,w,l,r
    N = boxes_corner.shape[0]
    boxes3d_corner = np.zeros((N, 8, 3))
    boxes3d_corner[:, 0:4, 0:2] = boxes_corner
    boxes3d_corner[:, 4:8, 0:2] = boxes_corner
    boxes3d_center = corner_to_center_box3d(boxes3d_corner, coordinate=coordinate, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)

    return boxes3d_center[:, [0, 1, 4, 5, 6]]

def corner_to_standup_box2d(boxes_corner):
    # (N, 4, 2) -> (N, 4) x1, y1, x2, y2
    N = boxes_corner.shape[0]
    standup_boxes2d = np.zeros((N, 4))
    standup_boxes2d[:, 0] = np.min(boxes_corner[:, :, 0], axis=1)
    standup_boxes2d[:, 1] = np.min(boxes_corner[:, :, 1], axis=1)
    standup_boxes2d[:, 2] = np.max(boxes_corner[:, :, 0], axis=1)
    standup_boxes2d[:, 3] = np.max(boxes_corner[:, :, 1], axis=1)

    return standup_boxes2d

# TODO: 0/90 may be not correct
def anchor_to_standup_box2d(anchors):
    # (N, 4) -> (N, 4) x,y,w,l -> x1,y1,x2,y2
    anchor_standup = np.zeros_like(anchors)
    # r == 0
    anchor_standup[::2, 0] = anchors[::2, 0] - anchors[::2, 3] / 2
    anchor_standup[::2, 1] = anchors[::2, 1] - anchors[::2, 2] / 2
    anchor_standup[::2, 2] = anchors[::2, 0] + anchors[::2, 3] / 2
    anchor_standup[::2, 3] = anchors[::2, 1] + anchors[::2, 2] / 2
    # r == pi/2
    anchor_standup[1::2, 0] = anchors[1::2, 0] - anchors[1::2, 2] / 2
    anchor_standup[1::2, 1] = anchors[1::2, 1] - anchors[1::2, 3] / 2
    anchor_standup[1::2, 2] = anchors[1::2, 0] + anchors[1::2, 2] / 2
    anchor_standup[1::2, 3] = anchors[1::2, 1] + anchors[1::2, 3] / 2

    return anchor_standup

def corner_to_center_box3d(boxes_corner, coordinate='camera', T_VELO_2_CAM=None, R_RECT_0=None):
    # (N, 8, 3) -> (N, 10) x,y,z,h,w,l,q0-q3

    # if coordinate == 'lidar':
    #     for idx in range(len(boxes_corner)):
    #         boxes_corner[idx] = lidar_to_camera_point(boxes_corner[idx], T_VELO_2_CAM, R_RECT_0)
    ret = []
    for roi in boxes_corner:
        if cfg.CORNER2CENTER_AVG:  # average version
            roi = np.array(roi)
            h = abs(np.sum(roi[:4, 1] - roi[4:, 1]) / 4)
            w = np.sum(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]])**2))
            ) / 4
            l = np.sum(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]])**2))
            ) / 4
            x = np.sum(roi[:, 0], axis=0)/ 8
            y = np.sum(roi[0:4, 1], axis=0)/ 4
            z = np.sum(roi[:, 2], axis=0)/ 8
            ry = np.sum(
                math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
                math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
                math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
                math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
                math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
                math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
                math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
                math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
            ) / 8
            if w > l:
                w, l = l, w
                ry = angle_in_limit(ry + np.pi / 2)
        else:  # max version
            h = max(abs(roi[:4, 1] - roi[4:, 1]))
            w = np.max(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]])**2))
            )
            l = np.max(
                np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]])**2)) +
                np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]])**2))
            )
            x = np.sum(roi[:, 0], axis=0)/ 8
            y = np.sum(roi[0:4, 1], axis=0)/ 4
            z = np.sum(roi[:, 2], axis=0)/ 8
            ry = np.sum(
                math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
                math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
                math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
                math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
                math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
                math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
                math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
                math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
            ) / 8
            if w > l:
                w, l = l, w
                ry = angle_in_limit(ry + np.pi / 2)
        ret.append([x, y, z, h, w, l, ry])
    if coordinate == 'lidar':
        ret = camera_to_lidar_box(np.array(ret), T_VELO_2_CAM, R_RECT_0)

    return np.array(ret)

# this just for visualize and testing
def lidar_box3d_to_camera_box(boxes3d, cal_projection=False, P2 = None, T_VELO_2_CAM=None):
    # (N, 10) or (N,7) -> (N, 4)/(N, 8, 2)  x,y,z,h,w,l,q0-q3 -> x1,y1,x2,y2/8*(x, y)
    num = len(boxes3d)
    boxes2d = np.zeros((num, 4), dtype=np.int32)
    projections = np.zeros((num, 8, 2), dtype=np.float32)

    lidar_boxes3d_corner = center_to_corner_box3d(boxes3d, coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM)
    for n in range(num):
        box3d = lidar_boxes3d_corner[n]
        box3d = lidar_to_camera_point(box3d, T_VELO_2_CAM)
        #points = np.hstack((box3d, np.ones((8, 1)))).T  # (8, 4) -> (4, 8)
        points = np.matmul(P2, box3d).T
        points[:, 0] /= points[:, 2]
        points[:, 1] /= points[:, 2]

        projections[n] = points[:, 0:2]
        minx = int(np.min(points[:, 0]))
        maxx = int(np.max(points[:, 0]))
        miny = int(np.min(points[:, 1]))
        maxy = int(np.max(points[:, 1]))

        boxes2d[n, :] = minx, miny, maxx, maxy

    return projections if cal_projection else boxes2d

def lidar_to_bird_view_img(lidar, factor=1):
    # Input:
    #   lidar: (N', 4)
    # Output:
    #   birdview: (w, l, 3)
    birdview = np.zeros(
        (cfg.INPUT_HEIGHT * factor, cfg.INPUT_WIDTH * factor, 1))
    for point in lidar:
        x, y = point[0:2]
        if cfg.X_MIN < x < cfg.X_MAX and cfg.Y_MIN < y < cfg.Y_MAX:
            x, y = int((x - cfg.X_MIN) / cfg.VOXEL_X_SIZE *
                       factor), int((y - cfg.Y_MIN) / cfg.VOXEL_Y_SIZE * factor)
            birdview[y, x] += 1
    birdview = birdview - np.min(birdview)
    divisor = np.max(birdview) - np.min(birdview)
    # TODO: adjust this factor
    birdview = np.clip((birdview / divisor * 255) *
                       5 * factor, a_min=0, a_max=255)
    birdview = np.tile(birdview, 3).astype(np.uint8)

    return birdview

def draw_lidar_box3d_on_image(img, boxes3d, scores, gt_boxes3d=np.array([]),
                              color=(0, 255, 255), gt_color=(255, 0, 255), thickness=1, P2 = None, T_VELO_2_CAM=None):
    # Input:
    #   img: (h, w, 3)
    #   boxes3d (N, 7) [x, y, z, h, w, l, r]
    #   scores
    #   gt_boxes3d (N, 10) [x, y, z, h, w, l, q0-q3]
    img = img.copy()
    #print(f'predicted boxes3d:{boxes3d.shape},ground truth boxes3d:{gt_boxes3d.shape}')
    projections = lidar_box3d_to_camera_box(boxes3d, cal_projection=True, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM)
    #print('begin to draw ground truth box.')
    gt_projections = lidar_box3d_to_camera_box(gt_boxes3d, cal_projection=True, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM)
    #print(f'gt_projections:{gt_projections[0]}')
    # draw projections
    for qs in projections:
        for k in range(0, 4):
            i, j = k, (k + 1) % 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), color, thickness, cv2.LINE_AA)

            i, j = k + 4, (k + 1) % 4 + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), color, thickness, cv2.LINE_AA)

            i, j = k, k + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), color, thickness, cv2.LINE_AA)
    # draw gt projections
    for qs in gt_projections:
        for k in range(0, 4):
            i, j = k, (k + 1) % 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), gt_color, thickness, cv2.LINE_AA)

            i, j = k + 4, (k + 1) % 4 + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), gt_color, thickness, cv2.LINE_AA)

            i, j = k, k + 4
            cv2.line(img, (qs[i, 0], qs[i, 1]), (qs[j, 0],
                                                 qs[j, 1]), gt_color, thickness, cv2.LINE_AA)

    return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2RGB)

def draw_lidar_box3d_on_birdview(birdview, boxes3d, scores, gt_boxes3d=np.array([]),
                                 color=(0, 255, 255), gt_color=(255, 0, 255), thickness=1, factor=1, P2 = None, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   birdview: (h, w, 3)
    #   boxes3d (N, 7) [x, y, z, h, w, l, r]
    #   scores
    #   gt_boxes3d (N, 7) [x, y, z, h, w, l, r]
    img = birdview.copy()
    corner_boxes3d = center_to_corner_box3d(boxes3d, coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    corner_gt_boxes3d = center_to_corner_box3d(gt_boxes3d, coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    # draw gt
    for box in corner_gt_boxes3d:
        x0, y0 = lidar_to_bird_view(*box[0, 0:2], factor=factor)
        x1, y1 = lidar_to_bird_view(*box[1, 0:2], factor=factor)
        x2, y2 = lidar_to_bird_view(*box[2, 0:2], factor=factor)
        x3, y3 = lidar_to_bird_view(*box[3, 0:2], factor=factor)

        cv2.line(img, (int(x0), int(y0)), (int(x1), int(y1)),
                 gt_color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)),
                 gt_color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x2), int(y2)), (int(x3), int(y3)),
                 gt_color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x3), int(y3)), (int(x0), int(y0)),
                 gt_color, thickness, cv2.LINE_AA)

    # draw detections
    for box in corner_boxes3d:
        x0, y0 = lidar_to_bird_view(*box[0, 0:2], factor=factor)
        x1, y1 = lidar_to_bird_view(*box[1, 0:2], factor=factor)
        x2, y2 = lidar_to_bird_view(*box[2, 0:2], factor=factor)
        x3, y3 = lidar_to_bird_view(*box[3, 0:2], factor=factor)

        cv2.line(img, (int(x0), int(y0)), (int(x1), int(y1)),
                 color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x1), int(y1)), (int(x2), int(y2)),
                 color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x2), int(y2)), (int(x3), int(y3)),
                 color, thickness, cv2.LINE_AA)
        cv2.line(img, (int(x3), int(y3)), (int(x0), int(y0)),
                 color, thickness, cv2.LINE_AA)

    return cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2RGB)

def label_to_gt_box3d(labels, cls='Car'):
    # Input:
    #   label: (N, N',11)
    #   cls: 'Car' or 'Pedestrain' or 'Cyclist'
    #   coordinate: 'camera' or 'lidar'
    # Output:
    #   (N, N', 10)
    #print(f'raw labels:{labels.shape}')
    boxes3d = []
    if cls == 'Car':
        acc_cls = ['Car']
    elif cls == 'Pedestrian':
        acc_cls = ['Pedestrian']
    elif cls == 'Cyclist':
        acc_cls = ['Cyclist']
    else: # all
        acc_cls = []

    acc_cls = [get_class_id(x) for x in acc_cls]

    for label in labels:
        boxes3d_a_label = []
        for row in label:
            #print(f'row:{row}')
            if row[10] in acc_cls or acc_cls == []:
                box3d = row[0:10]
                #print(f'boxe3d:{box3d.shape}')
                boxes3d_a_label.append(box3d)

        boxes3d.append(np.array(boxes3d_a_label).reshape(-1, 10))

    #print(f'through label to gt box3d:{boxes3d}')
    return boxes3d

def box3d_to_label(tag, batch_box3d, batch_cls, batch_score=[], coordinate='camera', P2 = None, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   (N, N', 7) x y z h w l r
    #   (N, N')
    #   cls: (N, N') 'Car' or 'Pedestrain' or 'Cyclist'
    #   coordinate(input): 'camera' or 'lidar'
    # Output:
    #   label: (N, N') N batches and N lines
    batch_label = []
    calib_dir = "{}/{}/calibration".format(cfg.DATA_DIR, 'validation')
    _, Tr, P = load_calib(os.path.join(calib_dir, tag + '.json'))
    R_RECT_0 = cfg.MATRIX_R_RECT_0

    if batch_score:
        template = '{} ' + ' '.join(['{:.4f}' for i in range(15)]) + '\n'
        for boxes, scores, clses in zip(batch_box3d, batch_score, batch_cls):
            label = []
            for box, score, cls in zip(boxes, scores, clses):
                if coordinate == 'camera':
                    box3d = box
                    box2d = lidar_box3d_to_camera_box(
                        camera_to_lidar_box(box[np.newaxis, :].astype(np.float32), T_VELO_2_CAM, R_RECT_0), cal_projection=False, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)[0]
                else:
                    box3d = lidar_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), Tr, R_RECT_0)[0]
                    box2d = lidar_box3d_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), cal_projection=False, P2=P, T_VELO_2_CAM=Tr)[0]
                x, y, z, h, w, l, r = box3d
                box3d = [h, w, l, x, y, z, r]
                label.append(template.format(cls, 0, 0, 0, *box2d, *box3d, float(score)))
            batch_label.append(label)
    else:
        template = '{} ' + ' '.join(['{:.4f}' for i in range(14)]) + '\n'
        for boxes, clses in zip(batch_box3d, batch_cls):
            label = []
            for box, cls in zip(boxes, clses):
                if coordinate == 'camera':
                    box3d = box
                    box2d = lidar_box3d_to_camera_box(
                        camera_to_lidar_box(box[np.newaxis, :].astype(np.float32), T_VELO_2_CAM, R_RECT_0), cal_projection=False,  P2=P2, T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)[0]
                else:
                    box3d = lidar_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), T_VELO_2_CAM, R_RECT_0)[0]
                    box2d = lidar_box3d_to_camera_box(
                        box[np.newaxis, :].astype(np.float32), cal_projection=False, P2=P2, T_VELO_2_CAM=T_VELO_2_CAM)[0]
                # x, y, z, h, w, l, r = box3d
                # box3d = [h, w, l, x, y, z, r]
                # label.append(template.format(cls, 0, 0, 0, *box2d, *box3d))
                label = np.concatenate([box3d, cls], axis=-1)
                #print(f'label in data aug: {label.shape}')
            batch_label.append(label)

    return np.array(batch_label)

def cal_anchors(cfg):
    # Output:
    #   anchors: (w, l, 2, 7) x y z h w l q0 q1 q2 q3
    x = np.linspace(cfg.X_MIN, cfg.X_MAX, cfg.FEATURE_WIDTH)
    y = np.linspace(cfg.Y_MIN, cfg.Y_MAX, cfg.FEATURE_HEIGHT)
    cx, cy = np.meshgrid(x, y)
    # all is (w, l, 2)cal_anchors
    cx = np.tile(cx[..., np.newaxis], 2)
    cy = np.tile(cy[..., np.newaxis], 2)
    cz = np.ones_like(cx) * cfg.ANCHOR_Z
    w = np.ones_like(cx) * cfg.ANCHOR_W
    l = np.ones_like(cx) * cfg.ANCHOR_L
    h = np.ones_like(cx) * cfg.ANCHOR_H
    r = np.ones_like(cx)
    r[..., 0] = 0  # 0
    r[..., 1] = 90 / 180 * np.pi  # 90

    # 7*(w,l,2) -> (w, l, 2, 7)
    anchors = np.stack([cx, cy, cz, h, w, l, r], axis=-1)

    return anchors

def quat_to_mat(quat):
    q = quat.copy()
    q=np.array(q)
    n = np.dot(q, q)
    if n < np.finfo(q.dtype).eps:
        rot_matrix=np.identity(4)
        return rot_matrix
    q = q * np.sqrt(2.0 / n)
    q = np.outer(q, q)
    rot_matrix = np.array(
        [[1.0 - q[2, 2] - q[3, 3], q[1, 2] + q[3, 0], q[1, 3] - q[2, 0]],
         [q[1, 2] - q[3, 0], 1.0 - q[1, 1] - q[3, 3], q[2, 3] + q[1, 0]],
         [q[1, 3] + q[2, 0], q[2, 3] - q[1, 0], 1.0 - q[1, 1] - q[2, 2]]],
        dtype=q.dtype)
    return rot_matrix

def mat_to_ang(R):
    q = np.zeros(4)
    K = np.zeros([4, 4])
    K[0, 0] = 1 / 3 * (R[0, 0] - R[1, 1] - R[2, 2])
    K[0, 1] = 1 / 3 * (R[1, 0] + R[0, 1])
    K[0, 2] = 1 / 3 * (R[2, 0] + R[0, 2])
    K[0, 3] = 1 / 3 * (R[1, 2] - R[2, 1])
    K[1, 0] = 1 / 3 * (R[1, 0] + R[0, 1])
    K[1, 1] = 1 / 3 * (R[1, 1] - R[0, 0] - R[2, 2])
    K[1, 2] = 1 / 3 * (R[2, 1] + R[1, 2])
    K[1, 3] = 1 / 3 * (R[2, 0] - R[0, 2])
    K[2, 0] = 1 / 3 * (R[2, 0] + R[0, 2])
    K[2, 1] = 1 / 3 * (R[2, 1] + R[1, 2])
    K[2, 2] = 1 / 3 * (R[2, 2] - R[0, 0] - R[1, 1])
    K[2, 3] = 1 / 3 * (R[0, 1] - R[1, 0])
    K[3, 0] = 1 / 3 * (R[1, 2] - R[2, 1])
    K[3, 1] = 1 / 3 * (R[2, 0] - R[0, 2])
    K[3, 2] = 1 / 3 * (R[0, 1] - R[1, 0])
    K[3, 3] = 1 / 3 * (R[0, 0] + R[1, 1] + R[2, 2])
    D, V = np.linalg.eig(K)
    pp = 0
    for i in range(1, 4):
        if (D[i] > D[pp]):
            pp = i
    q = V[:, pp]
    x = q[3]
    y = q[0]
    z = q[1]
    w = q[2]
    rol = math.atan2(2 * (w * x + y * z), 1 - 2 * (x * x + y * y))  # the rol is the yaw angle!
    # pith = math.asin(2*(w*y-z*z))
    # yaw = math.atan2(2*(w*z+x*y),1-2*(z*z+y*y))

    return rol

def gt_boxes3d_to_yaw(batch_boxes, T_VELO_2_CAM):
    # Input: (N, N', 10)
    # Output: (N, N', 7)
    #print(f'prepare to convert into yaw:{len(batch_boxes)}')
    batch_boxes_yaw = []
    for boxes in batch_boxes:
        boxes_yaw = []
        #print(f'boxes:{boxes}')
        for box in boxes:
            #print(f'box:{box.shape},{box}')
            center_point = np.insert(box[0:3], 3, values=1)
            #print(f'center point shape:{center_point.shape},{center_point}')
            center_point = np.matmul(T_VELO_2_CAM, center_point)
            #(f'center point shape:{center_point.shape}')

            quaternion = box[6:10]
            #print(f'quaternion: {quaternion.shape}, {quaternion}')
            rotation_mat = quat_to_mat(quaternion)
            rotation_mat = np.matmul(T_VELO_2_CAM[:,:3], rotation_mat)
            yaw = mat_to_ang(rotation_mat)

            box_yaw = np.hstack((center_point, box[3:6], yaw))
            #print(f'len of new box in yaw:{box_yaw.shape}')
            boxes_yaw.append(box_yaw)

        #print(f'boxes:{len(boxes_yaw)}')
        batch_boxes_yaw.append(np.array(boxes_yaw).reshape(-1, 7))

    #print(f'result batch boxes in yaw:{len(batch_boxes_yaw)}')

    return batch_boxes_yaw

def gt_boxes3d_to_yaw(batch_boxes):
    # Input: (N, N', 10)
    # Output: (N, N', 7)
    #print(f'prepare to convert into yaw:{len(batch_boxes)}')
    batch_boxes_yaw = []
    for boxes in batch_boxes:
        boxes_yaw = []
        #print(f'boxes:{boxes}')
        for box in boxes:
            #print(f'box:{box.shape},{box}')

            quaternion = box[6:10]
            #print(f'quaternion: {quaternion.shape}')
            _, _, yaw = qaut_to_angle(quaternion)

            box_yaw = np.hstack((box[0:6], yaw))
            #print(f'len of new box in yaw:{box_yaw.shape}')
            boxes_yaw.append(box_yaw)

        #print(f'boxes:{len(boxes_yaw)}')
        batch_boxes_yaw.append(np.array(boxes_yaw).reshape(-1, 7))

    #print(f'result batch boxes in yaw:{len(batch_boxes_yaw)}')

    return batch_boxes_yaw

def cal_rpn_target(labels, feature_map_shape, anchors, cls='Car', coordinate='lidar'):
    # Input:
    #   labels: (N, N')
    #   feature_map_shape: (w, l)
    #   anchors: (w, l, 2, 7)
    # Output:
    #   pos_equal_one (N, w, l, 2)
    #   neg_equal_one (N, w, l, 2)
    #   targets (N, w, l, 14)
    # attention: cal IoU on birdview
    batch_size = labels.shape[0]
    batch_gt_boxes3d = label_to_gt_box3d(labels, cls=cls)

    # projection gt_boxes3d from 10 dimension to 7 dimension (x y z h w l q0-3 -> x y z h w l r)
    batch_gt_boxes3d = gt_boxes3d_to_yaw(batch_gt_boxes3d)
    #print('finish converting label box to xy plane.')
    # defined in eq(1) in 2.2
    anchors_reshaped = anchors.reshape(-1, 7)
    anchors_d = np.sqrt(anchors_reshaped[:, 4]**2 + anchors_reshaped[:, 5]**2)
    pos_equal_one = np.zeros((batch_size, *feature_map_shape, 2), dtype=np.float32)
    neg_equal_one = np.zeros((batch_size, *feature_map_shape, 2), dtype=np.float32)
    targets = np.zeros((batch_size, *feature_map_shape, 14), dtype=np.float32)

    for batch_id in range(batch_size):
        # BOTTLENECK
        anchors_standup_2d = anchor_to_standup_box2d(
            anchors_reshaped[:, [0, 1, 4, 5]])
        # BOTTLENECK
        gt_standup_2d = corner_to_standup_box2d(center_to_corner_box2d(
            batch_gt_boxes3d[batch_id][:, [0, 1, 4, 5, 6]], coordinate=coordinate))

        iou = bbox_overlaps(
            np.ascontiguousarray(anchors_standup_2d).astype(np.float32),
            np.ascontiguousarray(gt_standup_2d).astype(np.float32),
        )
        # iou = cal_box3d_iou(
        #     anchors_reshaped,
        #     batch_gt_boxes3d[batch_id]
        # )

        # find anchor with highest iou(iou should also > 0)
        id_highest = np.argmax(iou.T, axis=1)
        id_highest_gt = np.arange(iou.T.shape[0])
        mask = iou.T[id_highest_gt, id_highest] > 0
        id_highest, id_highest_gt = id_highest[mask], id_highest_gt[mask]

        # find anchor iou > cfg.XXX_POS_IOU
        id_pos, id_pos_gt = np.where(iou > cfg.RPN_POS_IOU)

        # find anchor iou < cfg.XXX_NEG_IOU
        id_neg = np.where(np.sum(iou < cfg.RPN_NEG_IOU,
                                 axis=1) == iou.shape[1])[0]

        id_pos = np.concatenate([id_pos, id_highest])
        id_pos_gt = np.concatenate([id_pos_gt, id_highest_gt])

        # TODO: uniquify the array in a more scientific way
        id_pos, index = np.unique(id_pos, return_index=True)
        id_pos_gt = id_pos_gt[index]
        id_neg.sort()

        # cal the target and set the equal one
        index_x, index_y, index_z = np.unravel_index(
            id_pos, (*feature_map_shape, 2))
        pos_equal_one[batch_id, index_x, index_y, index_z] = 1

        # ATTENTION: index_z should be np.array
        targets[batch_id, index_x, index_y, np.array(index_z) * 7] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 0] - anchors_reshaped[id_pos, 0]) / anchors_d[id_pos]
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 1] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 1] - anchors_reshaped[id_pos, 1]) / anchors_d[id_pos]
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 2] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 2] - anchors_reshaped[id_pos, 2]) / cfg.ANCHOR_H
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 3] = np.log(
            batch_gt_boxes3d[batch_id][id_pos_gt, 3] / anchors_reshaped[id_pos, 3])
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 4] = np.log(
            batch_gt_boxes3d[batch_id][id_pos_gt, 4] / anchors_reshaped[id_pos, 4])
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 5] = np.log(
            batch_gt_boxes3d[batch_id][id_pos_gt, 5] / anchors_reshaped[id_pos, 5])
        targets[batch_id, index_x, index_y, np.array(index_z) * 7 + 6] = (
            batch_gt_boxes3d[batch_id][id_pos_gt, 6] - anchors_reshaped[id_pos, 6])

        index_x, index_y, index_z = np.unravel_index(
            id_neg, (*feature_map_shape, 2))
        neg_equal_one[batch_id, index_x, index_y, index_z] = 1
        # to avoid a box be pos/neg in the same time
        index_x, index_y, index_z = np.unravel_index(
            id_highest, (*feature_map_shape, 2))
        neg_equal_one[batch_id, index_x, index_y, index_z] = 0

    return pos_equal_one, neg_equal_one, targets

# BOTTLENECK
def delta_to_boxes3d(deltas, anchors, coordinate='lidar'):
    # Input:
    #   deltas: (N, w, l, 14)
    #   feature_map_shape: (w, l)
    #   anchors: (w, l, 2, 7)

    # Ouput:
    #   boxes3d: (N, w*l*2, 7)
    anchors_reshaped = anchors.reshape(-1, 7)
    deltas = deltas.reshape(deltas.shape[0], -1, 7)
    anchors_d = np.sqrt(anchors_reshaped[:, 4]**2 + anchors_reshaped[:, 5]**2)
    boxes3d = np.zeros_like(deltas)
    boxes3d[..., [0, 1]] = deltas[..., [0, 1]] * \
        anchors_d[:, np.newaxis] + anchors_reshaped[..., [0, 1]]
    boxes3d[..., [2]] = deltas[..., [2]] * \
        cfg.ANCHOR_H + anchors_reshaped[..., [2]]
    boxes3d[..., [3, 4, 5]] = np.exp(
        deltas[..., [3, 4, 5]]) * anchors_reshaped[..., [3, 4, 5]]
    boxes3d[..., 6] = deltas[..., 6] + anchors_reshaped[..., 6]

    return boxes3d

def point_transform(points, tx, ty, tz, rx=0, ry=0, rz=0):
    # Input:
    #   points: (N, 3)
    #   rx/y/z: in radians
    # Output:
    #   points: (N, 3)
    N = points.shape[0]
    points = np.hstack([points, np.ones((N, 1))])

    mat1 = np.eye(4)
    mat1[3, 0:3] = tx, ty, tz
    points = np.matmul(points, mat1)

    if rx != 0:
        mat = np.zeros((4, 4))
        mat[0, 0] = 1
        mat[3, 3] = 1
        mat[1, 1] = np.cos(rx)
        mat[1, 2] = -np.sin(rx)
        mat[2, 1] = np.sin(rx)
        mat[2, 2] = np.cos(rx)
        points = np.matmul(points, mat)

    if ry != 0:
        mat = np.zeros((4, 4))
        mat[1, 1] = 1
        mat[3, 3] = 1
        mat[0, 0] = np.cos(ry)
        mat[0, 2] = np.sin(ry)
        mat[2, 0] = -np.sin(ry)
        mat[2, 2] = np.cos(ry)
        points = np.matmul(points, mat)

    if rz != 0:
        mat = np.zeros((4, 4))
        mat[2, 2] = 1
        mat[3, 3] = 1
        mat[0, 0] = np.cos(rz)
        mat[0, 1] = -np.sin(rz)
        mat[1, 0] = np.sin(rz)
        mat[1, 1] = np.cos(rz)
        points = np.matmul(points, mat)

    return points[:, 0:3]

def box_transform(boxes, tx, ty, tz, r=0, coordinate='lidar'):
    # Input:
    #   boxes: (N, 10) x y z h w l q0-3
    # Output:
    #   boxes: (N, 10) x y z h w l q0-3

    boxes_corner = center_to_corner_box3d(boxes, coordinate=coordinate)  # (N, 8, 3)
    for idx in range(len(boxes_corner)):
        if coordinate == 'lidar':
            boxes_corner[idx] = point_transform(
                boxes_corner[idx], tx, ty, tz, rz=r)
        else:
            boxes_corner[idx] = point_transform(
                boxes_corner[idx], tx, ty, tz, ry=r)

    return corner_to_center_box3d(boxes_corner, coordinate=coordinate)

def rotate_label(label, rx=0, ry=0, rz=0):

    if rx != 0:
        mat = np.zeros((3, 3))
        mat[0, 0] = 1
        mat[1, 1] = np.cos(rx)
        mat[1, 2] = -np.sin(rx)
        mat[2, 1] = np.sin(rx)
        mat[2, 2] = np.cos(rx)

    if ry != 0:
        mat = np.zeros((3, 3))
        mat[1, 1] = 1
        mat[0, 0] = np.cos(ry)
        mat[0, 2] = np.sin(ry)
        mat[2, 0] = -np.sin(ry)
        mat[2, 2] = np.cos(ry)

    if rz != 0:
        mat = np.zeros((3, 3))
        mat[2, 2] = 1
        mat[0, 0] = np.cos(rz)
        mat[0, 1] = -np.sin(rz)
        mat[1, 0] = np.sin(rz)
        mat[1, 1] = np.cos(rz)

    for i,row in enumerate(label):
      translation = row[0:3]
      quaternion = row[6:10]
      roll, pitch, yaw = qaut_to_angle(quaternion)
      rot_quaternion = angle_to_quat(roll, pitch, yaw-rz)
      row[6:10] = rot_quaternion
      row[0:3] = np.matmul(translation, mat)
    return label

def cal_iou2d(box1, box2, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   box1/2: x, y, w, l, r
    # Output :
    #   iou
    buf1 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    buf2 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    tmp = center_to_corner_box2d(np.array([box1, box2]), coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    box1_corner = batch_lidar_to_bird_view(tmp[0]).astype(np.int32)
    box2_corner = batch_lidar_to_bird_view(tmp[1]).astype(np.int32)
    buf1 = cv2.fillConvexPoly(buf1, box1_corner, color=(1,1,1))[..., 0]
    buf2 = cv2.fillConvexPoly(buf2, box2_corner, color=(1,1,1))[..., 0]
    indiv = np.sum(np.absolute(buf1-buf2))
    share = np.sum((buf1 + buf2) == 2)
    if indiv == 0:
        return 0.0 # when target is out of bound
    return share / (indiv + share)

def cal_z_intersect(cz1, h1, cz2, h2):
    b1z1, b1z2 = cz1 - h1 / 2, cz1 + h1 / 2
    b2z1, b2z2 = cz2 - h2 / 2, cz2 + h2 / 2
    if b1z1 > b2z2 or b2z1 > b1z2:
        return 0
    elif b2z1 <= b1z1 <= b2z2:
        if b1z2 <= b2z2:
            return h1 / h2
        else:
            return (b2z2 - b1z1) / (b1z2 - b2z1)
    elif b1z1 < b2z1 < b1z2:
        if b2z2 <= b1z2:
            return h2 / h1
        else:
            return (b1z2 - b2z1) / (b2z2 - b1z1)

def cal_iou3d(box1, box2, T_VELO_2_CAM=None, R_RECT_0=None):
    # Input:
    #   box1/2: x, y, z, h, w, l, r
    # Output:
    #   iou
    buf1 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    buf2 = np.zeros((cfg.INPUT_HEIGHT, cfg.INPUT_WIDTH, 3))
    tmp = center_to_corner_box2d(np.array([box1[[0,1,4,5,6]], box2[[0,1,4,5,6]]]), coordinate='lidar', T_VELO_2_CAM=T_VELO_2_CAM, R_RECT_0=R_RECT_0)
    box1_corner = batch_lidar_to_bird_view(tmp[0]).astype(np.int32)
    box2_corner = batch_lidar_to_bird_view(tmp[1]).astype(np.int32)
    buf1 = cv2.fillConvexPoly(buf1, box1_corner, color=(1,1,1))[..., 0]
    buf2 = cv2.fillConvexPoly(buf2, box2_corner, color=(1,1,1))[..., 0]
    share = np.sum((buf1 + buf2) == 2)
    area1 = np.sum(buf1)
    area2 = np.sum(buf2)

    z1, h1, z2, h2 = box1[2], box1[3], box2[2], box2[3]
    z_intersect = cal_z_intersect(z1, h1, z2, h2)

    return share * z_intersect / (area1 * h1 + area2 * h2 - share * z_intersect)

def cal_box3d_iou(boxes3d, gt_boxes3d, cal_3d=0, T_VELO_2_CAM=None, R_RECT_0=None):
    # Inputs:
    #   boxes3d: (N1, 7) x,y,z,h,w,l,r
    #   gt_boxed3d: (N2, 7) x,y,z,h,w,l,r
    # Outputs:
    #   iou: (N1, N2)
    N1 = len(boxes3d)
    N2 = len(gt_boxes3d)
    output = np.zeros((N1, N2), dtype=np.float32)

    for idx in range(N1):
        for idy in range(N2):
            if cal_3d:
                output[idx, idy] = float(
                    cal_iou3d(boxes3d[idx], gt_boxes3d[idy]), T_VELO_2_CAM, R_RECT_0)
            else:
                output[idx, idy] = float(
                    cal_iou2d(boxes3d[idx, [0, 1, 4, 5, 6]], gt_boxes3d[idy, [0, 1, 4, 5, 6]], T_VELO_2_CAM, R_RECT_0))

    return output

def cal_box2d_iou(boxes2d, gt_boxes2d, T_VELO_2_CAM=None, R_RECT_0=None):
    # Inputs:
    #   boxes2d: (N1, 5) x,y,w,l,r
    #   gt_boxes2d: (N2, 5) x,y,w,l,r
    # Outputs:
    #   iou: (N1, N2)
    N1 = len(boxes2d)
    N2 = len(gt_boxes2d)
    output = np.zeros((N1, N2), dtype=np.float32)
    for idx in range(N1):
        for idy in range(N2):
            output[idx, idy] = cal_iou2d(boxes2d[idx], gt_boxes2d[idy], T_VELO_2_CAM, R_RECT_0)

    return output


'''
File to be used to test CRUD operations on the api.

The data for the Create operation can be passed as command line
arguments, or simply ignored as defaults are put in place.
'''

import requests, argparse
from json import dumps as _

parser = argparse.ArgumentParser()
parser.add_argument('-n', '--name', help='The name of the Person object to be created through the api')
parser.add_argument('-e', '--email', help = 'The email of the Person object to be created through the api')
parser.add_argument('-b', '--bio', help='The bio of the Person object to be created through the api')

args = parser.parse_args()

if not args.name and not args.email and not args.bio:
	import sys
	print(f"\nThis file comes with a CLI, run `python {sys.argv[0]} -h` to view the help option\n")

# provided name or default value
name = args.name or 'Jonathan Ma'
email = args.email or 'Joma@gmail.com'
bio = args.bio or 'Tech youtuber.'

# endpoints
list_create = 'http://tegarorobi.pythonanywhere.com/api/'
retrieve_update_destroy = f'http://tegarorobi.pythonanywhere.com/api/{name}/'

print(f"Create Person object: {name}")
persons_create = requests.post(list_create, {
	'name':name,
	'email':args.email or 'Joma@gmail.com',
	'bio':args.bio or 'Tech youtuber.'})
if persons_create.status_code == 400:
	import sys
	sys.exit(_(persons_create.json(), indent=4))
print(_(persons_create.json(), indent=4), '\n\n')

print("Person objects' list:")
persons_list = requests.get(list_create)
print(_(persons_list.json(), indent=4), '\n\n')

print(f"Retrieve Person object: {name}")
person_retrieve = requests.get(retrieve_update_destroy)
print(_(person_retrieve.json(), indent=4), '\n\n')

print(f"Update Person object: {name}")
person_update = requests.patch(retrieve_update_destroy, {'email':'updated@domain.com'})
print(_(person_update.json(), indent=4), '\n\n')

print(f"Delete Person obect: {name}")
person_delete = requests.delete(retrieve_update_destroy)
print('\n')

print("Person objects' list:")
persons_list = requests.get(list_create)
print(_(persons_list.json(), indent=4), '\n\n')


import argparse
import re
import logging

"""
This script checks whether the results format for Task 5 is correct.
It also provides some warnings about possible errors.

The correct format of the Task 5 results file is the following:
<line_number> <TAB> <score>

where <line_number> is the number of the claim in the debate
and <score> indicates the degree of 'check-worthiness' of the given line.
"""

_LINE_PATTERN_A = re.compile('^[1-9][0-9]{16,22}\t([-+]?\d*\.\d+|\d+)$')
logging.basicConfig(format='%(levelname)s : %(message)s', level=logging.INFO)

def check_format(file_path):
    with open(file_path, encoding='UTF-8') as out:
        file_content = out.read().strip()
        for i, line in enumerate(file_content.split('\n')):
            topic_id, tweet_id, score, run_id = line.strip().split('\t')

            if not _LINE_PATTERN_A.match("%s\t%s"%(tweet_id, score)):
                # 1. Check line format.
                logging.error("Wrong line format: {}".format(line))
                return False
            tweet_id = int(tweet_id)
            score = float(score.strip())

    return True

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--pred_file_path", "-p", required=True, help="The absolute path to the file you want to check.", type=str)
    args = parser.parse_args()
    logging.info("Task 5: Checking file: {}".format(args.pred_file_path))
    check_format(args.pred_file_path)


from selenium import webdriver
from selenium.webdriver.common.by import By

driver=webdriver.Chrome(executable_path="D:\\Python_Atul\\chromedriver\\chromedriver.exe")

driver.get("https://rahulshettyacademy.com/AutomationPractice/")
validatetext="ATUL"
driver.find_element(by=By.CSS_SELECTOR,value='#name').send_keys(validatetext)
driver.find_element(by=By.CSS_SELECTOR,value="#alertbtn").click()

alert=driver.switch_to.alert
alerttext=alert.text
assert validatetext in alerttext
alert.accept()


import tkinter as tk
import math

# ---------------------------- CONSTANTS ------------------------------- #
PINK = "#e2979c"
RED = "#e7305b"
GREEN = "#9bdeac"
YELLOW = "#f7f5dd"
FONT_NAME = "Courier"
WORK_MIN = 25
SHORT_BREAK_MIN = 5
LONG_BREAK_MIN = 20
reps = 0
timer = None

# ---------------------------- TIMER RESET ------------------------------- #
def reset_timer():
    global reps
    reps = 0

    window.after_cancel(timer)
    canvas.itemconfig(timer_text, text="00:00")
    title.config(text="Timer", fg=GREEN)
    tick_mark.config(text="")

# ---------------------------- TIMER MECHANISM -------------------------------
def start_timer():
    global reps
    reps += 1

    work_sec = WORK_MIN * 60
    short_break_sec = SHORT_BREAK_MIN * 60
    long_break_sec = LONG_BREAK_MIN * 60

    if reps % 8 == 0:
        count_down(long_break_sec)
        title.config(text="Break", fg=RED)
    elif reps % 2 == 0:
        count_down(short_break_sec)
        title.config(text="Break", fg=PINK)
    else:
        count_down(work_sec)
        title.config(text="Work", fg=GREEN)

# ---------------------------- COUNTDOWN MECHANISM ------------------------------- #
def count_down(count):

    global timer

    count_min = math.floor(count / 60)
    count_sec = count % 60
    if count_sec < 10:
        count_sec = f"0{count_sec}"

    canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}")
    if count > 0:
        timer = window.after(1000, count_down, count - 1)
    else:
        start_timer()
        marks = ""
        work_sessions = math.floor(reps/2)
        for _ in range(work_sessions):
            marks += "✔"
        tick_mark.config(text=marks)

# ---------------------------- UI SETUP ------------------------------- #
if __name__ == '__main__':
    window = tk.Tk()
    window.title("Pomodoro")
    window.config(padx=100, pady=100, bg=YELLOW)

    # Create title
    title = tk.Label(text="Timer", bg=YELLOW, fg=GREEN, font=(FONT_NAME, 45, "normal"))
    title.grid(column=1, row=0)

    # Create and place tomato image
    canvas = tk.Canvas(width=200, height=224, bg=YELLOW, highlightthickness=0)
    tomato_img = tk.PhotoImage(file="tomato.png")
    canvas.create_image(100, 112, image=tomato_img)
    timer_text = canvas.create_text(100, 130, text="00:00", fill="white", font=(FONT_NAME, 35, "bold"))
    canvas.grid(column=1, row=1)

    # create start button
    start_btn = tk.Button(text="Start", command=start_timer)
    start_btn.grid(column=0, row=2)

    # create reset button
    reset_btn = tk.Button(text="Reset", command=reset_timer)
    reset_btn.grid(column=2, row=2)

    # check mark
    tick_mark = tk.Label(bg=YELLOW, fg=GREEN, font=(FONT_NAME, 25, "normal"))
    tick_mark.grid(column=1, row=3)

    window.mainloop()


# -*- coding: utf-8 -*-
"""
Created on Wed Jul 13 19:35:50 2022

@author: BioCraze
"""
import csv
import os

import pandas as pd

#files = [i for i in os.listdir("C:/Users/BioCraze/Documents/Ruthazer lab/glial training/scripts/data/data-peaks/") if i.endswith('neuropil.csv')]
rootdir = 'E:\\glia training\\neuropil_notraining'

files =[f.path for f in os.scandir(rootdir) if f.path.endswith('.csv')]

cumulative = []
for file in files:
    df = pd.read_csv(file)
    data = df["Mean"]
    data_list = data.to_list()
    #ready = pd.concat([pd.Series(file), data])
    #cumulative.append(list(ready))
    new_csv = open("E:\\glia training\\neuropil_notraining\\transposed\\" + file[file.find("A"):-4] + ".csv",'w',newline='')
    write_to = csv.writer(new_csv)
    write_to.writerow(data_list)
    new_csv.close()
#for row in cumulative:
#    write_to.writerows([row])
#new_csv.close()


#3260ms
#153692KB

import sys

sdoku = []
for i in range(9):
    temp = list(map(int, sys.stdin.readline().split()))
    sdoku.append(temp)

bin = []
#빈칸 미리 찾아놓기
for i in range(9):
    for j in range(9):
        if sdoku[i][j] == 0:
            bin.append((i, j))

output = []

def checkRow(x, num):
    for i in range(9):
        if sdoku[x][i] == num:
            return False
    return True

def checkCol(y, num):
    for i in range(9):
        if sdoku[i][y] == num:
            return False
    return True

def check33(x, y, num):
    #3X3 검증 위함
    n = x // 3 * 3
    m = y // 3 * 3
    for i in range(3):
        for j in range(3):
            if sdoku[n+i][m+j] == num:
                return False
    return True

def sdoku_f(bin_index):
    #1. exit condition
    if bin_index == len(bin):
        for i in range(9):
            print(*sdoku[i])
        exit(0) #return으로 하면 모든 경우의 수 출력함

    #2. process(filtering)
    for num in range(1, 10):
        x = bin[bin_index][0]
        y = bin[bin_index][1]

        if checkRow(x, num) and checkCol(y, num) and check33(x, y, num):
                sdoku[x][y] = num #스도쿠에 수 입력
                #3. recursion
                sdoku_f(bin_index+1)
                sdoku[x][y] = 0 # 못들어가면 스도쿠 초기화

sdoku_f(0)

'''
#1. exit condition 에서
for i in range(9):
    print(*sdoku[i])
로 sdoku를 바로 출력하면 정상적으로 풀이된 sdoku식을 출력함

반면 #1. exit condition 에서
output.append(sdoku)으로 output배열에 sdoku객체를 넣고 함수실행이 끝난 뒤
output을 출력하면 0으로 초기화된 원본 스도쿠판을 출력함

이는 output.append(sdoku)할때 해당 배열 값을 저장하는게 아니라 sdoku객체를 저장하기 때문에
함수 밖에서 output을 출력하면
마지막 함수 실행이 끝날 때 sdoku[x][y] = 0 문으로 초기화 된 sdoku값이 출력되는 것.

'''


from datetime import datetime

from ..call_logs import CallLogsCommand
from hamcrest import assert_that
from hamcrest import equal_to
from xivo_lib_rest_client.tests.command import RESTCommandTestCase

class TestCallLogs(RESTCommandTestCase):

    Command = CallLogsCommand

    csvdata = u"Call Date,Caller,Called,Period,user Field\r\n2015-06-29T12:01:00.725871,John (1000),1234567890,0,\r\n"

    def test_list(self):
        self.session.get.return_value = self.new_response(200, body=self.csvdata)

        result = self.command.list()

        self.session.get.assert_called_once_with(self.base_url,
                                                 params={},
                                                 headers={'Accept': 'text/csv'})
        assert_that(result, equal_to(self.csvdata))

    def test_list_with_dates(self):
        self.session.get.return_value = self.new_response(200, body=self.csvdata)

        expected_params = {
            'start_date': '2015-01-01T12:13:14',
            'end_date': '2015-01-02T12:13:14',
        }

        self.command.list(start_date=datetime(2015, 1, 1, 12, 13, 14),
                          end_date=datetime(2015, 1, 2, 12, 13, 14))

        self.session.get.assert_called_once_with(self.base_url,
                                                 params=expected_params,
                                                 headers={'Accept': 'text/csv'})

    def test_when_not_200(self):
        self.session.get.return_value = self.new_response(404)

        self.assertRaisesHTTPError(self.command.list)


import pandas as pd
import torch

from dataset import Dataset
from model import LSTMStockPriceModel

if __name__ == '__main__':
    batch_size = 16
    lr = 0.0001
    epochs = 100

    # set device to cuda if available
    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

    print('Set up Data Loader...')
    df = pd.read_csv('../data/stocks_prices_prep.csv', sep=';')#.sample(frac=1).head(4)
    train_set = Dataset(df, seq_len=30)
    train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, shuffle=False, drop_last=True)
    print(f'Series length: {len(train_set)}')

    print('Loaded model to device...')
    model = LSTMStockPriceModel().float()
    model = model.to(device)

    total_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
    print(f'Trainable model parameters: {total_params}')

    print('Setup Adam optimizer...')
    optimizer = torch.optim.Adam(
        model.parameters(),
        lr=lr,
    )

    print('Setup loss function...')
    loss = torch.nn.MSELoss(reduction='sum').to(device)
    monitor_loss = torch.nn.L1Loss()

    print('Start train loop...')
    for epoch in range(1, epochs+1):
        epoch_loss = 0
        epoch_monitor_loss = 0

        # reset state every epoch
        state = None

        # iter over batches
        for x, y in train_loader:
            # move data to device
            x = x.to(device)
            y = y[:, 0, :].to(device)

            # get prediction
            state = None
            y_pred, state = model(x, state)

            # compute loss
            batch_loss = loss(y_pred, y)
            epoch_loss += batch_loss
            batch_monitor_loss = monitor_loss(y_pred, y)
            epoch_monitor_loss += batch_monitor_loss

            # perform gradient step
            model.zero_grad()
            batch_loss.backward()
            optimizer.step()

        print(f'EPOCH: {epoch} of {epochs} with MSELoss: {epoch_loss/len(train_set):.5f} and MAELoss: {epoch_monitor_loss/len(train_set):.5f}')

    print('Save model...')
    torch.save(model.state_dict(), 'lstm.t7')


import os, sys
import matplotlib.pyplot as plt
import json
from utils import init_root

def load_data_file(path: str):
    with open(path, "r", encoding="ascii") as f:
        text = f.read()

    try:
        return json.loads(text)
    except:
        ret = {}
        for line in text.split("\n"):
            line = line.strip()
            if len(line) > 1:
                k, v = line.split(": ")
                ret[k] = [float(x) for x in v[1:-1].split(", ")]
        return ret

def plot(axis, models):
    root_dir = init_root()

    plt.title(axis)
    plt.xlabel("Epoch")
    plt.ylabel("Accuracy (%)" if "Acc" in axis else "Loss")

    for model in models:
        data = load_data_file(os.path.join(root_dir, "vis_lang", model, "raw_data"))[
            axis
        ]
        plt.plot(list(range(len(data))), data, label=model)

    plt.legend()
    plt.show()

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python plot.py <name_of_axis> <model1> <model2> ...")
        exit()

    axis = sys.argv[1]
    models = sys.argv[2:]

    plot(axis, models)


from fastapi import APIRouter, Depends, HTTPException, status
import database, models, schemas
from sqlalchemy.orm import Session

get_db = database.get_db

router = APIRouter(
    prefix='/dataentry',
    tags=['dataentry']
)

@router.post('/')
async def data_entry(request: schemas.Data, db: Session = Depends(get_db)):
    new_form_data = models.DataEntry(writer=request.writer, formId=request.formId,
                                   answer=request.answer)
    db.add(new_form_data)
    db.commit()
    db.refresh(new_form_data)
    return HTTPException(status_code=status.HTTP_201_CREATED)

@router.get('/answers')
async def send_all_answers(db: Session = Depends(get_db)):
    answers = db.query(models.DataEntry).all()
    return answers


from aiogram import types, Dispatcher
from telegram import markups
from aiogram.dispatcher import FSMContext

async def start_command(message: types.Message, state: FSMContext):
    if message.chat.type != 'private':  # start only in private messages
        return
    await state.finish()
    keyboard = markups.get_start_menu()
    await message.answer("👋Добро пожаловать в бот для сбора данных с телеграмм груп!\n"
                         "🧐Через меня вы сможете получить информацию о пользователях",
                         reply_markup=keyboard)

def register_handlers(dp: Dispatcher):
    """Register message handlers"""
    dp.register_message_handler(start_command, commands="start")


# Literally following this tutorial: https://www.youtube.com/watch?v=vzabeKdW9tE

DEBUG = True

if DEBUG:
    from PIL import Image
    import numpy as np

    def read_image(path):
        return np.asarray(Image.open(path).convert('L'))

    def write_image(image, path):
        img = Image.fromarray(np.array(image), 'L')
        img.save(path)

DATA_DIR = "data/"
TEST_DIR = "test/"
TEST_IMAGES = DATA_DIR + "t10k-images.idx3-ubyte"
TEST_LABELS = DATA_DIR + "t10k-labels.idx1-ubyte"
TRAIN_IMAGES = DATA_DIR + "train-images.idx3-ubyte"
TRAIN_LABELS = DATA_DIR + "train-labels.idx1-ubyte"

# data_chars = "/data/archive/A_Z Handrwritten Data.csv"

def bytes_to_int(byte_data):
    return int.from_bytes(byte_data, "big")

def read_images(filename, max_n_images=None):
    images = []
    with open(filename, "rb") as file:
        _ = file.read(4)  # first 4 bytes, giving meta infos
        n_images = bytes_to_int(file.read(4))
        if max_n_images:
            n_images = max_n_images
        n_rows = bytes_to_int(file.read(4))
        n_columns = bytes_to_int(file.read(4))
        for image_index in range(n_images):
            image = []
            for row_index in range(n_rows):
                row = []
                for column_index in range(n_columns):
                    pixel = file.read(1)
                    row.append(pixel)
                image.append(row)
            images.append(image)
    return images

def read_labels(filename, max_n_labels=None):
    labels = []
    with open(filename, "rb") as file:
        _ = file.read(4)  # first 4 bytes, giving meta infos
        n_labels = bytes_to_int(file.read(4))
        if max_n_labels:
            n_labels = max_n_labels
        for label_index in range(n_labels):
            label = file.read(1)
            labels.append(label)
    return labels

def flatten_list(data_list):
    return [pixel for sublist in data_list for pixel in sublist]

def extract_features(data_set):
    return [flatten_list(data) for data in data_set]

def dist(x, y):
    return sum(
        [
            (bytes_to_int(x_i) - bytes_to_int(y_i)) ** 2
            for x_i, y_i in zip(x, y)]
    ) ** 0.5

def get_training_distances_for_test_sample(X_train, test_sample):
    return [dist(train_sample, test_sample) for train_sample in X_train]

def get_most_frequent_element(l):
    return max(l, key=l.count)

def knn(X_train, y_train, X_test, k=3):
    y_pred = []
    for test_sample_index, test_sample in enumerate(X_test):
        training_distances = get_training_distances_for_test_sample(X_train, test_sample)
        sorted_distance_indices = [pair[0] for pair in sorted(enumerate(training_distances), key=lambda x: x[1])]
        candidates = [bytes_to_int(y_train[index]) for index in sorted_distance_indices[:k]]
        top_candidate = get_most_frequent_element(candidates)
        y_pred.append(top_candidate)
    return y_pred

def main():
    X_train = read_images(TRAIN_IMAGES, 1000)
    y_train = read_labels(TRAIN_LABELS, 1000)
    X_test = read_images(TEST_IMAGES, 5)
    y_test = read_labels(TEST_LABELS, 5)

    if DEBUG:
        for index, test_sample in enumerate(X_test):
            write_image(test_sample, f"{TEST_DIR}{index}.png")

    X_train = extract_features(X_train)
    X_test = extract_features(X_test)

    y_pred = knn(X_train, y_train, X_test, 3)

    for y_pred_i, y_test_i in zip(y_test, y_pred):
        print(y_pred_i)
        print(y_test_i)

    accuracy = sum([
        int(bytes_to_int(y_pred_i) == y_test_i)
        for y_pred_i, y_test_i
        in zip(y_test, y_pred)
    ]) / len(y_test)

    print(y_pred)
    print(f"Accuracy is {accuracy}")

if __name__ == "__main__":
    main()


import csv
import random
import os
import uuid
import xml.etree.ElementTree as ET
from argparse import ArgumentParser
from multiprocessing import cpu_count
from multiprocessing.pool import ThreadPool
from typing import List, Tuple
from zipfile import ZipFile

DEFAULT_ARC_COUNT = 50
DEFAULT_ARC_FILES = 100
DEFAULT_PROCESSES_COUNT = cpu_count()

class XmlCsvProcessor:
    def __init__(self, zip_path: str, csv_path: str) -> None:
        self._zip_path = zip_path
        self._csv_path = csv_path

    def gen_xml_archives(self, arc_count: int = DEFAULT_ARC_COUNT,
                         files_in_arc: int = DEFAULT_ARC_FILES) -> None:
        '''
        Generates archives with XML files
        :param arc_count: number of result archives
        :param files_in_arc: number of files in one archive
        '''
        if not os.path.exists(self._zip_path):
            os.mkdir(self._zip_path)

        for _ in range(arc_count):
            self._gen_archive(files_in_arc)

    def _gen_archive(self, files_in_arc: int) -> None:
        rand_zip_path = self._rand_zip_path()

        with ZipFile(rand_zip_path, mode='w') as arc:
            for i in range(files_in_arc):
                root = XmlCsvProcessor._gen_single_xml()
                self._write_xml_archive(root, arc, i)

    def _rand_zip_path(self) -> str:
        while True:
            zip_name = XmlCsvProcessor._rand_str() + '.zip'
            full_zip_path = os.path.join(self._zip_path, zip_name)
            if not os.path.exists(full_zip_path):
                return full_zip_path

    @staticmethod
    def _gen_single_xml() -> ET.Element:
        root = ET.Element('root')

        var_id = ET.Element(
            'var', attrib={'name': 'id', 'value': str(uuid.uuid4())})
        root.append(var_id)
        var_level = ET.Element(
            'var',
            attrib={'name': 'level', 'value': str(random.randint(1, 100))}
        )
        root.append(var_level)

        objs = ET.Element('objects')
        for _ in range(random.randint(1, 10)):
            obj = ET.Element(
                'object', attrib={'name': XmlCsvProcessor._rand_str()})
            objs.append(obj)

        root.append(objs)
        return root

    @staticmethod
    def _rand_str() -> str:
        result = []

        for _ in range(random.randint(4, 10)):
            ch = chr(ord('a') + random.randint(0, 25))
            result.append(ch)

        return ''.join(result)

    def _write_xml_archive(self, root: ET.Element, arc: ZipFile, i: int) -> None:
        xml_str = ET.tostring(root)
        arc.writestr(f'{i}.xml', xml_str)

    def gen_csv_files(self,
                      processes_count: int = DEFAULT_PROCESSES_COUNT) -> None:
        '''
        Generates CSV files from prepared XMLs in archives
        :param processes_count: parallelism level
        '''
        if not os.path.exists(self._csv_path):
            os.mkdir(self._csv_path)

        with ThreadPool(processes=processes_count) as pool:
            results = pool.map(
                self._process_single_zip, os.listdir(self._zip_path))

        levels_path = os.path.join(self._csv_path, 'levels.csv')
        names_path = os.path.join(self._csv_path, 'names.csv')

        with open(levels_path, 'w') as lf, open(names_path, 'w') as nf:
            levels_writer = csv.DictWriter(lf, fieldnames=('id', 'level'))
            levels_writer.writeheader()
            names_writer = csv.DictWriter(nf, fieldnames=('id', 'object_name'))
            names_writer.writeheader()

            for res in results:
                for id_, level, object_ids_names in res:
                    levels_writer.writerow({'id': id_, 'level': level})
                    for name in object_ids_names:
                        names_writer.writerow({'id': id_, 'object_name': name})

    def _process_single_zip(self, zip_name: str) -> List[Tuple[str, int, List[str]]]:
        zip_path = os.path.join(self._zip_path, zip_name)
        result = []

        with ZipFile(zip_path) as zip_file:
            for name in zip_file.namelist():
                buf = zip_file.read(name)
                root = ET.fromstring(buf.decode())

                vars = root.findall('var')
                if vars[0].get('name') == 'id':
                    id_, level = vars[0].get('value'), int(vars[1].get('value'))
                else:
                    id_, level = vars[1].get('value'), int(vars[0].get('value'))

                objects = root.find('objects')
                object_ids_names = [obj.get('name') for obj in objects]

                result.append((id_, level, object_ids_names))

        return result

def main():
    parser = ArgumentParser()
    parser.add_argument(
        'arch_path', type=str, help='path for arhcives generation')
    parser.add_argument('csv_path', type=str, help='path for csv generation')
    args = parser.parse_args()

    processor = XmlCsvProcessor(
        zip_path=args.arch_path, csv_path=args.csv_path)

    processor.gen_xml_archives()
    processor.gen_csv_files()

if __name__ == '__main__':
    main()


import config
import requests

urlLine = 'https://notify-api.line.me/api/notify'
token = config.LINE_NOTIFY_TOKEN
headers = {
            'content-type':
            'application/x-www-form-urlencoded',
            'Authorization':'Bearer '+token
           }

def send_alert(msg = 'ทดสอบ'):
    r = requests.post(urlLine, headers=headers , data = {'message':msg})
    print(r.text)

def send_pic(path='',msg ='BUY'):
    file = {'imageFile':open(path+'.png','rb')}
    data = ({
            'message':msg
        })
    LINE_HEADERS = {"Authorization":"Bearer "+token}
    session = requests.Session()
    r=session.post(urlLine, headers=LINE_HEADERS, files=file, data=data)
    print(r.text)


import sentivi
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
from sentivi import Pipeline
from fastapi import Form
from fastapi.responses import HTMLResponse

class ResponseModel(BaseModel):
    polarity: int
    label: str

    class Config:
        schema_extra = {
            'example': [
                {
                    'polarity': 0,
                    'label': '#NEG',
                },
                {
                    'polarity': 1,
                    'label': '#NEU',
                },
                {
                    'polarity': 2,
                    'label': '#POS'
                }
            ]
        }

class RESTServiceGateway:
    server = FastAPI(
        title='Sentivi Web Services',
        description='A simple tool for sentiment analysis',
        version=sentivi.__version__
    )

    pipeline: Optional[Pipeline] = None

    tags_metadata = [
        {
            'name': 'Predictor',
            'description': 'Sentiment Predictor'
        }
    ]

    response_models = {
        'foo': {
            'polarity': 'Numeric polarity',
            'label': 'Label from piplines\' label set'
        }
    }

    def __init__(self, pipeline: Optional[Pipeline], *args, **kwargs):
        super(RESTServiceGateway, self).__init__(*args, **kwargs)

        RESTServiceGateway.pipeline = pipeline
        print('Initialized REST Service Gateway')

    @staticmethod
    @server.get('/', response_class=HTMLResponse)
    async def index():
        return '
Hello, World
'

    @staticmethod
    @server.post('/get_sentiment/', tags=['Predictor'], response_model=ResponseModel)
    async def get_sentiment(text: str = Form(...,
                                             title='Input text',
                                             description='Input text for sentiment analysis')):
        """
        POST method

        :param text:
        :return:
        """
        try:
            polarities = RESTServiceGateway.pipeline.predict([text])
            polarity = polarities[0]
            label = RESTServiceGateway.pipeline.decode_polarity(polarities)[0]
        except Exception as ex:
            polarity = -1
            label = 'ERROR'
            print(f'Error has occurred!!!\n{ex}')

        return {
            'polarity': polarity,
            'label': label
        }

    @staticmethod
    def get_server():
        return RESTServiceGateway.server


#!/usr/bin/env python
import rospy
import smach
from std_msgs.msg import String, Int64MultiArray
from utils import *
import parameters as param
from tf.transformations import euler_from_quaternion, quaternion_from_euler
from geometry_msgs.msg import (
    Pose,
    PoseStamped,
    PoseArray
)
from farming_bot_msgs.msg import (
    ModeSwitchRequestMessage
)

from nav_msgs.msg import (
    Odometry
)

modes = [
    ModeSwitchRequestMessage.NAVIGATION,
    ModeSwitchRequestMessage.START_NAVIGATION,
    ModeSwitchRequestMessage.STOP_NAVIGATION,
    ModeSwitchRequestMessage.KEY_HOLE_GENERATION,
    ModeSwitchRequestMessage.ASSISTED_ALIGNMENT,
    ModeSwitchRequestMessage.START_HARVESTING,
    ModeSwitchRequestMessage.STOP_HARVESTING,
    ModeSwitchRequestMessage.DO_HARVESTING,
]

class Navigation(smach.State): # Define a state, outcomes and userdata hooks
    def __init__(self):
        smach.State.__init__(self, outcomes=['full', 'navigate','align','try','done'],
                            input_keys=['in_navigation','start_nav','row_side','plan'], output_keys=['out_navigation','start_nav','row_side'])
        self.request_change_publisher = rospy.Publisher("/machine_inputs/control_mode/request_mode_change", ModeSwitchRequestMessage, queue_size=10)
        self.row_side_data = None
        self.plan = [['Navigate',[15,15,0]],['Harvest',[]],['Navigate',[25,15,0]],['Harvest',[]]]
        self.goal = [10,10,0]
        self.pub_goal = rospy.Publisher("/gplanner/goal", PoseStamped, queue_size=1)
        self.plan_data =rospy.Subscriber("/plan_to_execute", Int64MultiArray, self.receive_plan)
        self.track_odometry =rospy.Subscriber("/odometry/filtered_map", Odometry, self.track_harvey)
        self.track_odometry_cartsian =rospy.Subscriber("/odometry/filtered_map", Odometry, self.callback_start)
        self.mode_feedback_sub =rospy.Subscriber("/machine_state/control_mode/mode_change_feedback", ModeSwitchRequestMessage, self.mode_feedback)
        self.current_location = [0,0,0]
        self.distance_to_goal = None
        self.start_nav = False
    def callback_start(self, msg):
        orientation_q = msg.pose.pose.orientation
        orientation_list = [orientation_q.x,
                            orientation_q.y, orientation_q.z, orientation_q.w]
        (roll, pitch, yaw) = euler_from_quaternion(orientation_list)
        self.current_location = [msg.pose.pose.position.x, msg.pose.pose.position.y, yaw]
    def row_change(self,msg):
        self.row_side_data = msg.data

    def mode_change(self,value):
        self.request_change_publisher.publish(ModeSwitchRequestMessage(next_mode=value))
    def mode_feedback(self, msg):
        self.mode_feedback = msg.next_mode
    def receive_plan(self,msg):
        self.plan = msg.data

    def track_harvey(self,msg):
        orientation_q = msg.pose.pose.orientation
        orientation_list = [orientation_q.x,
                            orientation_q.y, orientation_q.z, orientation_q.w]
        (roll, pitch, yaw) = euler_from_quaternion(orientation_list)
        self.current_location = [msg.pose.pose.position.x, msg.pose.pose.position.y, yaw]
    def execute(self, userdata):
        # userdata.plan = self.plan
        print((userdata.plan))
        self.mode_change('START_NAVIGATION')

        while userdata.plan!=[]:
            plan = userdata.plan.pop(0)
            print(plan, plan[0])
            if plan[0] =='Navigate':
                rospy.loginfo(" I was here")
                # print(plan,plan[1])
                self.start_nav = True
                goal_to_achieve = convert_goal_pose(plan[1])
                rospy.sleep(2)
                publish = True
                while self.start_nav!=False:
                    self.distance_to_goal = euclidean_distance(self.current_location,plan[1])

                    if publish == True:
                        print('Publishing Goal and state')

                        self.pub_goal.publish(goal_to_achieve)
                        self.mode_change('START_NAVIGATION')
                        publish = False

                    # self.start_nav = False
                    # return 'try'
                    if self.distance_to_goal < param.threshold_distance_to_goal:
                        self.mode_change('STOP_NAVIGATION')
                        self.start_nav = False
                        publish = True
                        return 'try'
            elif plan[0] =='Harvest':
                # self.mode_change('START_HARVESTING')
                    return 'full'
            elif plan[0] == 'Align':
                # self.mode_change('ASSISTED_ALIGNMENT')
                return 'align'

        return 'try'

class Harvest(smach.State):
    def __init__(self):
        smach.State.__init__(self, outcomes=['done','harvesting'],
                            input_keys=['in_navigation','in_harvest','start_nav','row_side'],
                            output_keys=['out_navigation','in_harvest','out_harvest','start_nav','row_side'])
        # self.align_success
        self.request_change_publisher = rospy.Publisher("/machine_inputs/control_mode/request_mode_change", ModeSwitchRequestMessage, queue_size=10)
        self.mode_feedback_sub =rospy.Subscriber("/machine_state/control_mode/mode_change_feedback", ModeSwitchRequestMessage, self.mode_feedback)

    def mode_feedback(self, msg):
        self.mode_feedback = msg.next_mode
    def mode_change(self,value):
        self.request_change_publisher.publish(ModeSwitchRequestMessage(next_mode=value))

    def execute(self, userdata):
        if userdata.in_harvest >= 50:
            userdata.start_nav = False
            userdata.in_harvest = 0
            rospy.loginfo(userdata.start_nav)
            self.mode_change('STOP_HARVESTING')
            return 'done'

        else:
            userdata.out_harvest = userdata.in_harvest + 1
            rospy.sleep(0.5)
            # userdata.out_navigation = userdata.in_navigation - 1
            rospy.loginfo('harvest: '+str(userdata.in_harvest) + ', navigation: ' + str(userdata.in_navigation))
            self.mode_change('START_HARVESTING')
            return 'harvesting'

class Align(smach.State):
    def __init__(self):

        smach.State.__init__(self, outcomes=['align','harvesting'],
                            input_keys=['align','in_navigation','in_harvest','start_nav','row_side'],
                            output_keys=['align','out_navigation','out_harvest','start_nav','row_side'])
        self.request_change_publisher = rospy.Publisher("/machine_inputs/control_mode/request_mode_change", ModeSwitchRequestMessage, queue_size=10)

class Key_hole(smach.State):
    def __init__(self):

        smach.State.__init__(self, outcomes=['align','harvesting'],
                            input_keys=['align','in_navigation','in_harvest','start_nav','row_side'],
                            output_keys=['align','out_navigation','out_harvest','start_nav','row_side'])
        self.request_change_publisher = rospy.Publisher("/machine_inputs/control_mode/request_mode_change", ModeSwitchRequestMessage, queue_size=10)

    def mode_change(self,value):
        self.request_change_publisher.publish(ModeSwitchRequestMessage(next_mode=value))

    def execute(self, userdata):
        userdata.align = 0
        if userdata.align>= 50:
            userdata.start_nav = False
            # self.mode_change('START_HARVESTING')
            rospy.loginfo(userdata.start_nav)
            return 'harvesting'
        else:
            userdata.align =+ 1
            # userdata.out_navigation = userdata.in_navigation - 1
            rospy.loginfo('Align' + str(userdata.aligns))
            return 'align'

def init_harvey_userdata():
    #create SMACH state machine
    sm = smach.StateMachine(outcomes=['finished','aborted'])
    sm.userdata.sm_harvest = 0
    sm.userdata.sm_start_nav = False
    sm.userdata.sm_navigation = 0
    sm.userdata.sm_row_side = None
    sm.userdata.align = 0
    sm.userdata.plan = [['Harvest',[]],['Harvest',[]]]
    sm.set_initial_state(['Navigation'])

    with sm:
        smach.StateMachine.add('Navigation', Navigation(), # Add state and mapping for IO hooks
                transitions={'full':'Harvest','navigate':'Navigation','align': 'Align', 'try':'Navigation', 'done':'finished'},
                remapping={'in_navigation':'sm_navigation','out_navigation':'sm_navigation','start_nav':'sm_start_nav','row_side':'sm_row_side'})
        smach.StateMachine.add('Harvest', Harvest(),
                transitions={'done':'Navigation',
                             'harvesting':'Harvest',},
                remapping={'in_navigation':'sm_navigation','out_navigation':'sm_navigation',
                           'in_harvest':'sm_harvest','out_harvest':'sm_harvest','start_nav':'sm_start_nav','row_side':'sm_row_side'})
        smach.StateMachine.add('Align', Align(),
                transitions={'align':'Align',
                             'harvesting':'Harvest',},
                remapping={'in_navigation':'sm_navigation','out_navigation':'sm_navigation',
                           'in_harvest':'sm_harvest','out_harvest':'sm_harvest','start_nav':'sm_start_nav','row_side':'sm_row_side'})
    return sm

if __name__ == '__main__':
    rospy.init_node('harvey_smach_v1')
    rospy.sleep(0.5)          # Time to initialize to avoid missing log messages
    sm = init_harvey_userdata()  # Create state machine
    outcome = sm.execute()    # Execute state machine
    rospy.loginfo('Final outcome: '+outcome)
    rospy.spin()


import requests
import io
import os
from wordcloud import WordCloud
from requests.packages.urllib3.util.retry import Retry
from flask import Flask, redirect, url_for, render_template, request, Response
from flask_session import Session
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from helpers import apology, remove_punctuation
from bs4 import BeautifulSoup
from collections import Counter

# Configure application
app = Flask(__name__)

# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True

# Ensure responses aren't cached
@app.after_request
def after_request(response):
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Expires"] = 0
    response.headers["Pragma"] = "no-cache"
    return response

Session(app)

#Website blocks connection when hosted on Heroku
# Proxies not needed when running locally
proxies = {
"http": os.environ['QUOTAGUARDSTATIC_URL'],
"https": os.environ['QUOTAGUARDSTATIC_URL']
}

request_handler = requests.Session()

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        query = str(request.form.get("songlyrics"))
        return redirect(url_for('getlyrics', query = query))
    else:
        method = "GET"
        return render_template("index.html", method = method)

@app.route("/getlyrics", methods=["GET"])
def getlyrics():

    session = requests_retry(retries = 5, backoff_factor = 0.5)

    # Query database for query
    query = request.args.get('query', None)
    if ' ' in query:
            query = query.replace(' ', '+')
            song_url = 'https://search.azlyrics.com/search.php?q=' + query
    else:
            song_url = 'https://search.azlyrics.com/search.php?q=' + query

    #Mimic user headers, so website doesn't immediately reject connection
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'}
    response = session.get(song_url, headers = headers)

    soup = BeautifulSoup(response.content, 'html.parser')

    #Element selection changes based on whether or not albums appear in search results
    with_albums = soup.find_all('div', attrs= 'panel')
    without_albums = soup.find_all('td', attrs = {'class' : 'text-left visitedlyr'})

    #If album panel is also shown, select the song panel instead
    if len(with_albums) > 2:
        lyric_page = (with_albums[2].find('a').get('href'))
    elif len(with_albums) > 1:
        lyric_page = (with_albums[1].find('a').get('href'))
    elif without_albums:
        lyric_page = (without_albums[0].find('a').get('href'))
    else:
        print('Sorry, we could not find any results for that search. Please modify your search terms.' + '\n')

    response2 = session.get(lyric_page, headers = headers, proxies = proxies)

    #Grab the element from page that contains song lyrics
    #Grab title for item that the search query returned
    soup2 = BeautifulSoup(response2.content, 'html.parser')
    lyric_list = []
    lyrics = str(soup2.find('div', attrs = {'class':None, 'id':None}).get_text())
    for line in lyrics.split("\n"):
        lyric_list.append(line)
    songmetadata = str(soup2.find('title').getText()).split(' -')
    artist = songmetadata[0]
    songtitle = songmetadata[1].split(" |")[0].replace(" Lyrics", "").strip()

    word_count = Counter()

    for line in lyric_list:
        for word in line.split():
            word = word.lower()
            word = remove_punctuation(word)
            word_count[word] += 1

    words = word_count.most_common(12)

    labels = []
    values = []
    for i in range(len(words)):
        labels.append(words[i][0])
        values.append(words[i][1])

    wordcloud_text = ' '.join(map(str, lyric_list)).strip().lower()
    for word in wordcloud_text.split():
        word = remove_punctuation(word)

    return render_template("query.html", artist = artist, songtitle = songtitle, lyric_list = lyric_list, max=values[0], labels=labels, values=values, wordcloud_text = wordcloud_text)

#Add listener to generate wordcloud image dynamically in memory
#Does not work on Heroku due to ephemeral file system
#ToDo: Integrate AWS S3 file system in order to save file
@app.route('/image/<wordcloud_text>/plot.png')
def wordcloud(wordcloud_text):
    wordcloud = WordCloud(stopwords = " ", collocations = False).generate(wordcloud_text)
    img = io.BytesIO()
    wordcloud.to_image().save(img, 'PNG')
    img.seek(0)
    return Response(img.getvalue(), mimetype='image/png')

#Added to prevent occasional connection refusal, and lessen request workload
def requests_retry(retries, backoff_factor):
    session = requests.Session()
    retry = Retry(connect=retries, backoff_factor=backoff_factor)
    adapter = requests.adapters.HTTPAdapter(max_retries= retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)

    return session

def errorhandler(e):
    """Handle error"""
    if not isinstance(e, HTTPException):
        e = InternalServerError()
    return apology(e.name, e.code)

# Listen for errors
for code in default_exceptions:
    app.errorhandler(code)(errorhandler)


from odoo import models, fields, api

from odoo.tools import populate, pycompat

class TestPopulateModel(models.Model):
    _name = 'test.populate'
    _description = 'Test Populate'

    name = fields.Char(default='Foo')
    state = fields.Selection([('a', 'A'), ('b', 'B')], default='a')
    active = fields.Boolean('Active', default=True)
    category_id = fields.Many2one('test.populate.category', 'Category')
    some_ref = fields.Integer('Reference')
    dependant_field_1 = fields.Char('Dependant 1')
    dependant_field_2 = fields.Char('Dependant 2')
    sequence = fields.Integer("Sequence")

    _populate_dependencies = ['test.populate.category']

    _populate_sizes = {
        'small': 20,
        'medium': 30,
        'large': 100,
    }

    def _populate_factories(self):

        # cross dependant field in a sub generator, cartesian product of two fields
        dependant_factories = [
            ('dependant_field_1', populate.cartesian(['d1_1', 'd1_2'])),
            ('dependant_field_2', populate.cartesian(['d2_1', 'd2_2', 'd2_3_{counter}'])),
        ]
        def generate_dependant(iterator, *args):
            dependants_generator = populate.chain_factories(dependant_factories, self._name)
            for dependant_values in dependants_generator:
                values = next(iterator)
                yield {**dependant_values, **values, '__complete': values['__complete'] and dependant_values['__complete']}

        def get_name(values=None, counter=0, **kwargs):
            active = 'active' if values['active'] else 'inactive'
            cat = 'filling' if values['__complete'] else 'corner'
            return '%s_%s_%s' % (active, cat, counter)

        category_ids = self.env.registry.populated_models['test.populate.category']

        return [
            ('active', populate.cartesian([True, False], [3, 1])),
            ('state', populate.cartesian([False] + self.env['test.populate']._fields['state'].get_values(self.env))),
            ('some_ref', populate.iterate([False, 1, 2, 3, 4])),
            ('_dependant', generate_dependant),
            ('name', populate.compute(get_name)),
            ('category_id', populate.randomize([False] + category_ids)),
            ('sequence', populate.randint(1, 10))
        ]

class TestPopulateDependencyModel(models.Model):
    _name = 'test.populate.category'
    _description = 'Test Populate Category'

    _populate_sizes = {
        'small': 3,
        'medium': 10,
        'large': 20,
    }
    name = fields.Char('Name', required=True, default='Cat1')
    active = fields.Boolean('Active', default=True)

    def _populate_factories(self):
        return [
            ('active', populate.cartesian([True, False], [9, 1])),
            ('name', populate.cartesian(['Cat1', 'Cat2', 'Cat3'])),
        ]

class TestNoPopulateModelInherit(models.Model):
    _name = 'test.populate.inherit'
    _inherit = 'test.populate'

    _description = 'Test populate inherit'

    additionnal_field = fields.Char(required=True)

    def _populate_factories(self):
        return super()._populate_factories() + [
            ('additionnal_field', populate.iterate(['V1', 'V2', 'V3'])),
        ]

class TestNoPopulateModel(models.Model):
    _name = 'test.no.populate'
    _description = 'A model with no populate method and a required field, should not crash'

    name = fields.Char(required=True)


from collections import Counter

import requests

CAR_DATA = 'https://bites-data.s3.us-east-2.amazonaws.com/cars.json'

# pre-work: load JSON data into program

with requests.Session() as s:
    data = s.get(CAR_DATA).json()

# your turn:
def most_prolific_automaker(year):
    """Given year 'year' return the automaker that released
       the highest number of new car models"""

    data = [{"id":1,"automaker":"Dodge","model":"Ram Van 1500","year":1999},
            {"id":2,"automaker":"Chrysler","model":"Town & Country","year":2002},
            {"id":3,"automaker":"Porsche","model":"Cayenne","year":2008},
            {"id":1,"automaker":"Dodge","model":"Ram Van 1500","year":2002},
            {"id":2,"automaker":"Chrysler","model":"Town & Country","year":1999},
            {"id":3,"automaker":"Porsche","model":"Cayenne","year":2008},
            {"id":1,"automaker":"Dodge","model":"Ram Van 1500","year":1999},
            {"id":2,"automaker":"Chrysler","model":"Town & Country","year":2002},
            {"id":3,"automaker":"Porsche","model":"Cayenne","year":1999},
            ]

    data_filtered_by_year = [item for item in data if item['year'] == year]

    count_dict = {}

    for item in data_filtered_by_year:

        automaker = item['automaker']
        count_dict[automaker] = count_dict.setdefault(automaker, 0) + 1

    dict_items = count_dict.items()
    dict_items_sorted = sorted(dict_items, key=lambda x: x[1])

    return dict_items_sorted[-1][0]

def get_models(automaker, year):
    """Filter cars 'data' by 'automaker' and 'year',
       return a set of models (a 'set' to avoid duplicate models)"""

    data = [{"id":1,"automaker":"Dodge","model":"Ram Van 1500","year":1999},
            {"id":2,"automaker":"Chrysler","model":"Town & Country","year":2002},
            {"id":3,"automaker":"Porsche","model":"Cayenne","year":2008},
            {"id":1,"automaker":"Dodge","model":"Ram Van 1500","year":2002},
            {"id":2,"automaker":"Chrysler","model":"Town & Country","year":1999},
            {"id":3,"automaker":"Porsche","model":"Cayenne","year":2008},
            {"id":1,"automaker":"Dodge","model":"Ram Van 1500","year":1999},
            {"id":2,"automaker":"Chrysler","model":"Town & Country","year":2002},
            {"id":3,"automaker":"Porsche","model":"Cayenne","year":1999},
            ]

    data_filtered_by_year = [item for item in data if (item['year'] == year) & (item['automaker'] == automaker)]

    models = [item['model'] for item in data_filtered_by_year]

    return set(models)

count_dict = most_prolific_automaker(1999)

print(count_dict)

models = get_models('Porsche', 1999)

print(models)