import pandas as pd
import numpy as np
from PIL import Image
import os

path=os.path.abspath(".")
emotion_Yes=path+"\\image\\1"
emotion_No=path+"\\image\\0"

Y=[]
for i in os.listdir(emotion_Yes):
    im=Image.open(emotion_Yes+"\\"+i)
    im=im.resize((48,48))
    im=im.convert("L")
    Y.append(np.array(im))

N=[]
for i in os.listdir(emotion_No):
    im=Image.open(emotion_No+"\\"+i)
    im=im.resize((48,48))
    im=im.convert("L")
    N.append(np.array(im))

d=[]
for i in Y:
    d1=[]
    for j in i:
        for n in j:
            d1.append(n)
    d.append(str(d1).replace("[","").replace("]","").replace(",",""))

e=[]
for i in N:
    e1=[]
    for j in i:
        for n in j:
            e1.append(n)
    e.append(str(e1).replace("[","").replace("]","").replace(",",""))

dat1=pd.DataFrame({"emotion":[1]*len(d),"pixels":d})
jh1=["Training"]*int(dat1.shape[0]*0.7)+["PrivateTest"]*(int(dat1.shape[0]*0.2))+["PublicTest"]*(int(dat1.shape[0]-int(dat1.shape[0]*0.7)-int(dat1.shape[0]*0.2)))
#训练集、测试集、验证集=70%，20%，10%
dat1["Usage"]=jh1

dat2=pd.DataFrame({"emotion":[0]*len(e),"pixels":e})
jh2=["Training"]*int(dat2.shape[0]*0.7)+["PrivateTest"]*(int(dat2.shape[0]*0.2))+["PublicTest"]*(int(dat2.shape[0]-int(dat2.shape[0]*0.7)-int(dat2.shape[0]*0.2)))
dat2["Usage"]=jh2

data_x=pd.concat([dat1,dat2],ignore_index=True)
data_x.to_csv(path+"\\emotion_file.csv",index=False)

print(data_x.shape)


import os
import cv2
import glob as glob
from tqdm import tqdm

from dataset import CocoDataset

COLOR_PLATE_TRUE = (0,255,0)
COLOR_PLATE_PRED = (0,0,255)
COLOR_FACE_TRUE = (255,0,0)
COLOR_FACE_PRED = (0, 165, 255)
IMG_EXT = ['jpg', 'jpeg', 'png']

def draw_bbox_yolo1(img_path, txt_path, save_path, threshold = 0.1, label_viz = True):
    img = cv2.imread(img_path)
    height, width, _ = img.shape
    os.makedirs(save_path, exist_ok=True)

    with open(txt_path, 'r') as f:
        data = [x for x in f.read().strip().split('\n') if x != '']

    if len(data) == 0:
        return

    for line in data:
        bbox = [x for x in line.split(' ') if x != '']
        conf = float(bbox[-1]) if len(bbox) == 6 else 1

        if conf < threshold:
            print(conf, threshold)
            continue

        x_center, y_center = float(bbox[1]) * width, float(bbox[2]) * height
        box_width, box_height = float(bbox[3]) * width, float(bbox[4]) * height

        x_min = int(x_center - box_width/2)
        y_min = int(y_center - box_height/2)
        x_max = int(x_center + box_width/2)
        y_max = int(y_center + box_height/2)

        if bbox[0] in ['plate', '0'] : #plate
            color = COLOR_PLATE_TRUE if label_viz else COLOR_PLATE_PRED
        elif bbox[0] in ['face', '1']: #face
            color = COLOR_FACE_TRUE if label_viz else COLOR_FACE_PRED
        else:
            raise ValueError(f'not class: {bbox[0]}')

        img = cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color, 2)
        # if not label_viz:
            # blur_bbox(img, (x_min, y_min), (x_max, y_max))

    os.makedirs(save_path, exist_ok=True)
    cv2.imwrite(os.path.join(save_path, os.path.basename(img_path)),img)

def draw_bbox_yolov1_folder(img_root, true_txt_root, pred_txt_root, save_path, threshold=0.05, double_viz = True):
    pred_txt_root = pred_txt_root if double_viz else true_txt_root
    img_paths = sorted([x for x in glob.glob(f'{img_root}/**/*', recursive=True) if x.split('.')[-1].lower() in IMG_EXT])
    true_paths = sorted([x for x in glob.glob(f'{true_txt_root}/**/*.txt', recursive=True)])
    pred_paths = sorted([x for x in glob.glob(f'{pred_txt_root}/**/*.txt', recursive=True)])

    for index, img_path in enumerate(tqdm(img_paths)):
        img_name = os.path.splitext(img_path.split(f'{img_root}/')[-1])[0]
        true_path = f'{true_txt_root}/{img_name}.txt'
        pred_path = f'{pred_txt_root}/{img_name}.txt'

        if true_path not in true_paths or pred_path not in pred_paths:
            print(f'{img_name} txt file not found in directory')
            continue

        draw_bbox_yolo1(img_path, true_path, save_path, threshold = threshold)
        if double_viz:
            draw_bbox_yolo1(f'{save_path}/{img_path.split(f"{img_root}/")[-1]}', pred_path, save_path, threshold = threshold, label_viz=False)

def blur_bbox(img, top_left, bottom_right):
    x_min, y_min = top_left
    x_max, y_max = bottom_right
    bbox = img[y_min:y_max, x_min:x_max]
    bbox =  cv2.GaussianBlur(bbox,(51,51),cv2.BORDER_DEFAULT)

    img[y_min:y_max, x_min:x_max] = bbox

    return img

if __name__ == '__main__':
    import yaml

    with open("utils/configs/viz_config.yaml", 'r') as stream:
        try:
            config_dict = yaml.safe_load(stream)
        except yaml.YAMLError as exc:
            print(exc)

    for index in range(len(config_dict['true_annotation_path'])):
        coco_dataset = CocoDataset(
            true_annotation_path = config_dict['true_annotation_path'][index],
            pred_annotation_path = config_dict['pred_annotation_path'][index],
            save_path = config_dict['save_path'][index],
            src_img_root = config_dict['src_img_root'],
            dest_img_root = config_dict['dest_img_root'],
            )

        ## Visualize both onto images
        coco_dataset.visualize_images(annotation_type = 'bbox', conf_threshold = 0.05, size_threshold=144)


import time

import pytest
from koyo.timer import format_time, time_average, time_loop

@pytest.mark.parametrize(
    "value, expected",
    [
        (0.01, "us"),
        (0.1, "ms"),
        (0.5, "s"),
        (1, "s"),
        (60, "s"),
        (75, "min"),
        (3654, "hr"),
    ],
)
def test_format_time(value, expected):
    """Test 'format_time'"""
    result = format_time(value)
    assert expected in result

def test_time_average():
    """Test 'time_average'"""
    t_start = time.time()
    time.sleep(0.01)
    result = time_average(t_start, 1)
    assert "Avg:" in result
    assert "Tot:" in result
    assert result.startswith("[")
    assert result.endswith("]")

def test_time_loop():
    """Test 'time_loop'"""
    t_start = time.time()
    time.sleep(0.01)
    result = time_loop(t_start, 0, 0)
    assert "Avg:" in result
    assert "Rem:" in result
    assert "Tot:" in result
    assert "%" in result

    result = time_loop(t_start, 0, 0, as_percentage=False)
    assert "%" not in result
    assert result.startswith("[")
    assert result.endswith("]")


from django.shortcuts import redirect, render
import requests
from .models import City
from .forms import CityForm

def home(request):
    url = "http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=f960d50e283cb9ae7475ea657d2186d9"

    cities = City.objects.all()

    if request.method == 'POST':
        form = CityForm(request.POST)
        form.save()
    form = CityForm()

    weather_data = []

    for city in cities:

        city_weather = requests.get(url.format(city)).json()

        weather = {
            'city': city,
            'temperature': round(((city_weather['main']['temp']-32)*5/9), 2),
            'description': city_weather['weather'][0]['description'],
            'icon': city_weather['weather'][0]['icon']
        }

        weather_data.append(weather)

    context = {'weather_data': weather_data, 'form': form}

    return render(request, "index.html", context)

def clear(request):
    City.objects.all().delete()
    return redirect("home")


from __future__ import print_function
import numpy as np
import os, sys
import argparse
import easydict # pip install easydict
import cntk
from cntk import Trainer, load_model, Axis, input_variable, parameter, times, combine, \
    softmax, roipooling, plus, element_times, CloneMethod, alias, Communicator, reduce_sum
from cntk.core import Value
from cntk.io import MinibatchData
from cntk.initializer import normal
from cntk.layers import placeholder, Constant, Sequential
from cntk.learners import momentum_sgd, learning_parameter_schedule_per_sample, momentum_schedule
from cntk.logging import log_number_of_parameters, ProgressPrinter
from cntk.logging.graph import find_by_name, plot
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from _cntk_py import force_deterministic_algorithms

abs_path = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.join(abs_path, ".."))
from utils.rpn.rpn_helpers import create_rpn, create_proposal_target_layer, create_proposal_layer
from utils.annotations.annotations_helper import parse_class_map_file
from utils.od_mb_source import ObjectDetectionMinibatchSource
from utils.proposal_helpers import ProposalProvider
from FastRCNN.FastRCNN_train import clone_model, clone_conv_layers, create_fast_rcnn_predictor, \
    create_detection_losses

def prepare(cfg, use_arg_parser=True):
    cfg.MB_SIZE = 1
    cfg.NUM_CHANNELS = 3
    cfg.OUTPUT_PATH = os.path.join(abs_path, "Output")
    cfg["DATA"].MAP_FILE_PATH = os.path.join(abs_path, cfg["DATA"].MAP_FILE_PATH)
    running_locally = os.path.exists(cfg["DATA"].MAP_FILE_PATH)
    if running_locally:
        os.chdir(cfg["DATA"].MAP_FILE_PATH)
        if not os.path.exists(os.path.join(abs_path, "Output")):
            os.makedirs(os.path.join(abs_path, "Output"))
        if not os.path.exists(os.path.join(abs_path, "Output", cfg["DATA"].DATASET)):
            os.makedirs(os.path.join(abs_path, "Output", cfg["DATA"].DATASET))
    else:
        # disable debug and plot outputs when running on GPU cluster
        cfg["CNTK"].DEBUG_OUTPUT = False
        cfg.VISUALIZE_RESULTS = False

    if use_arg_parser:
        parse_arguments(cfg)

    data_path = cfg["DATA"].MAP_FILE_PATH
    if not os.path.isdir(data_path):
        raise RuntimeError("Directory %s does not exist" % data_path)

    cfg["DATA"].CLASS_MAP_FILE = os.path.join(data_path, cfg["DATA"].CLASS_MAP_FILE)
    cfg["DATA"].TRAIN_MAP_FILE = os.path.join(data_path, cfg["DATA"].TRAIN_MAP_FILE)
    cfg["DATA"].TEST_MAP_FILE = os.path.join(data_path, cfg["DATA"].TEST_MAP_FILE)
    cfg["DATA"].TRAIN_ROI_FILE = os.path.join(data_path, cfg["DATA"].TRAIN_ROI_FILE)
    cfg["DATA"].TEST_ROI_FILE = os.path.join(data_path, cfg["DATA"].TEST_ROI_FILE)

    cfg['MODEL_PATH'] = os.path.join(cfg.OUTPUT_PATH, "faster_rcnn_eval_{}_{}.model"
                                     .format(cfg["MODEL"].BASE_MODEL, "e2e" if cfg["CNTK"].TRAIN_E2E else "4stage"))
    cfg['BASE_MODEL_PATH'] = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", "PretrainedModels",
                                          cfg["MODEL"].BASE_MODEL_FILE)

    cfg["DATA"].CLASSES = parse_class_map_file(cfg["DATA"].CLASS_MAP_FILE)
    cfg["DATA"].NUM_CLASSES = len(cfg["DATA"].CLASSES)

    if cfg["CNTK"].FAST_MODE:
        cfg["CNTK"].E2E_MAX_EPOCHS = 1
        cfg["CNTK"].RPN_EPOCHS = 1
        cfg["CNTK"].FRCN_EPOCHS = 1

    if cfg["CNTK"].FORCE_DETERMINISTIC:
        force_deterministic_algorithms()
    np.random.seed(seed=cfg.RND_SEED)

    if False and cfg["CNTK"].DEBUG_OUTPUT:
        # report args
        print("Using the following parameters:")
        print("Flip image       : {}".format(cfg["TRAIN"].USE_FLIPPED))
        print("Train conv layers: {}".format(cfg.TRAIN_CONV_LAYERS))
        print("Random seed      : {}".format(cfg.RND_SEED))
        print("Momentum per MB  : {}".format(cfg["CNTK"].MOMENTUM_PER_MB))
        if cfg["CNTK"].TRAIN_E2E:
            print("E2E epochs       : {}".format(cfg["CNTK"].E2E_MAX_EPOCHS))
        else:
            print("RPN lr factor    : {}".format(cfg["CNTK"].RPN_LR_FACTOR))
            print("RPN epochs       : {}".format(cfg["CNTK"].RPN_EPOCHS))
            print("FRCN lr factor   : {}".format(cfg["CNTK"].FRCN_LR_FACTOR))
            print("FRCN epochs      : {}".format(cfg["CNTK"].FRCN_EPOCHS))

def parse_arguments(cfg):
    parser = argparse.ArgumentParser()
    parser.add_argument('-datadir', '--datadir', help='Data directory where the ImageNet dataset is located',
                        required=False, default=cfg["DATA"].MAP_FILE_PATH)
    parser.add_argument('-outputdir', '--outputdir', help='Output directory for checkpoints and models',
                        required=False, default=None)
    parser.add_argument('-logdir', '--logdir', help='Log file',
                        required=False, default=None)
    parser.add_argument('-n', '--num_epochs', help='Total number of epochs to train', type=int,
                        required=False, default=cfg["CNTK"].E2E_MAX_EPOCHS)
    parser.add_argument('-m', '--minibatch_size', help='Minibatch size', type=int,
                        required=False, default=cfg["CNTK"].MB_SIZE)
    parser.add_argument('-e', '--epoch_size', help='Epoch size', type=int,
                        required=False, default=cfg["CNTK"].NUM_TRAIN_IMAGES)
    parser.add_argument('-q', '--quantized_bits', help='Number of quantized bits used for gradient aggregation',
                        type=int,
                        required=False, default='32')
    parser.add_argument('-r', '--restart',
                        help='Indicating whether to restart from scratch (instead of restart from checkpoint file by default)',
                        action='store_true')
    parser.add_argument('-device', '--device', type=int, help="Force to run the script on a specified device",
                        required=False, default=None)
    parser.add_argument('-rpnLrFactor', '--rpnLrFactor', type=float, help="Scale factor for rpn lr schedule",
                        required=False)
    parser.add_argument('-frcnLrFactor', '--frcnLrFactor', type=float, help="Scale factor for frcn lr schedule",
                        required=False)
    parser.add_argument('-e2eLrFactor', '--e2eLrFactor', type=float, help="Scale factor for e2e lr schedule",
                        required=False)
    parser.add_argument('-momentumPerMb', '--momentumPerMb', type=float, help="momentum per minibatch", required=False)
    parser.add_argument('-e2eEpochs', '--e2eEpochs', type=int, help="number of epochs for e2e training", required=False)
    parser.add_argument('-rpnEpochs', '--rpnEpochs', type=int, help="number of epochs for rpn training", required=False)
    parser.add_argument('-frcnEpochs', '--frcnEpochs', type=int, help="number of epochs for frcn training",
                        required=False)
    parser.add_argument('-rndSeed', '--rndSeed', type=int, help="the random seed", required=False)
    parser.add_argument('-trainConv', '--trainConv', type=int, help="whether to train conv layers", required=False)
    parser.add_argument('-trainE2E', '--trainE2E', type=int, help="whether to train e2e (otherwise 4 stage)",
                        required=False)

    args = vars(parser.parse_args())

    if args['rpnLrFactor'] is not None:
        cfg["MODEL"].RPN_LR_FACTOR = args['rpnLrFactor']
    if args['frcnLrFactor'] is not None:
        cfg["MODEL"].FRCN_LR_FACTOR = args['frcnLrFactor']
    if args['e2eLrFactor'] is not None:
        cfg["MODEL"].E2E_LR_FACTOR = args['e2eLrFactor']
    if args['e2eEpochs'] is not None:
        cfg["CNTK"].E2E_MAX_EPOCHS = args['e2eEpochs']
    if args['rpnEpochs'] is not None:
        cfg["CNTK"].RPN_EPOCHS = args['rpnEpochs']
    if args['frcnEpochs'] is not None:
        cfg["CNTK"].FRCN_EPOCHS = args['frcnEpochs']
    if args['momentumPerMb'] is not None:
        cfg["CNTK"].MOMENTUM_PER_MB = args['momentumPerMb']
    if args['rndSeed'] is not None:
        cfg.RND_SEED = args['rndSeed']
    if args['trainConv'] is not None:
        cfg.TRAIN_CONV_LAYERS = True if args['trainConv'] == 1 else False
    if args['trainE2E'] is not None:
        cfg.TRAIN_E2E = True if args['trainE2E'] == 1 else False

    if args['datadir'] is not None:
        cfg["DATA"].MAP_FILE_PATH = args['datadir']
    if args['outputdir'] is not None:
        cfg.OUTPUT_PATH = args['outputdir']
    if args['logdir'] is not None:
        log_dir = args['logdir']
    if args['device'] is not None:
        # Setting one worker on GPU and one worker on CPU. Otherwise memory consumption is too high for a single GPU.
        if Communicator.rank() == 0:
            cntk.device.try_set_default_device(cntk.device.gpu(args['device']))
        else:
            cntk.device.try_set_default_device(cntk.device.cpu())

###############################################################
###############################################################

# Defines the Faster R-CNN network model for detecting objects in images
def create_faster_rcnn_model(features, scaled_gt_boxes, dims_input, cfg):
    # Load the pre-trained classification net and clone layers
    base_model = load_model(cfg['BASE_MODEL_PATH'])
    conv_layers = clone_conv_layers(base_model, cfg)
    fc_layers = clone_model(base_model, [cfg["MODEL"].POOL_NODE_NAME], [cfg["MODEL"].LAST_HIDDEN_NODE_NAME], clone_method=CloneMethod.clone)

    # Normalization and conv layers
    feat_norm = features - Constant([[[v]] for v in cfg["MODEL"].IMG_PAD_COLOR])
    conv_out = conv_layers(feat_norm)

    # RPN and prediction targets
    rpn_rois, rpn_losses = create_rpn(conv_out, scaled_gt_boxes, dims_input, cfg)
    rois, label_targets, bbox_targets, bbox_inside_weights = \
        create_proposal_target_layer(rpn_rois, scaled_gt_boxes, cfg)

    # Fast RCNN and losses
    cls_score, bbox_pred = create_fast_rcnn_predictor(conv_out, rois, fc_layers, cfg)
    detection_losses = create_detection_losses(cls_score, label_targets, bbox_pred, rois, bbox_targets, bbox_inside_weights, cfg)
    loss = rpn_losses + detection_losses
    pred_error = classification_error(cls_score, label_targets, axis=1)

    return loss, pred_error

def create_faster_rcnn_eval_model(model, image_input, dims_input, cfg, rpn_model=None):
    print("creating eval model")
    last_conv_node_name = cfg["MODEL"].LAST_CONV_NODE_NAME
    conv_layers = clone_model(model, [cfg["MODEL"].FEATURE_NODE_NAME], [last_conv_node_name], CloneMethod.freeze)
    conv_out = conv_layers(image_input)

    model_with_rpn = model if rpn_model is None else rpn_model
    rpn = clone_model(model_with_rpn, [last_conv_node_name], ["rpn_cls_prob_reshape", "rpn_bbox_pred"], CloneMethod.freeze)
    rpn_out = rpn(conv_out)
    # we need to add the proposal layer anew to account for changing configs when buffering proposals in 4-stage training
    rpn_rois = create_proposal_layer(rpn_out.outputs[0], rpn_out.outputs[1], dims_input, cfg)

    roi_fc_layers = clone_model(model, [last_conv_node_name, "rpn_target_rois"], ["cls_score", "bbox_regr"], CloneMethod.freeze)
    pred_net = roi_fc_layers(conv_out, rpn_rois)
    cls_score = pred_net.outputs[0]
    bbox_regr = pred_net.outputs[1]

    if cfg.BBOX_NORMALIZE_TARGETS:
        num_boxes = int(bbox_regr.shape[1] / 4)
        bbox_normalize_means = np.array(cfg.BBOX_NORMALIZE_MEANS * num_boxes)
        bbox_normalize_stds = np.array(cfg.BBOX_NORMALIZE_STDS * num_boxes)
        bbox_regr = plus(element_times(bbox_regr, bbox_normalize_stds), bbox_normalize_means, name='bbox_regr')

    cls_pred = softmax(cls_score, axis=1, name='cls_pred')
    eval_model = combine([cls_pred, rpn_rois, bbox_regr])

    return eval_model

def store_eval_model_with_native_udf(eval_model, cfg):
    import copy
    sys.path.append(os.path.join(abs_path, "..", "..", "Extensibility", "ProposalLayer"))
    cntk.ops.register_native_user_function('ProposalLayerOp',
                                           'Cntk.ProposalLayerLib-' + cntk.__version__.rstrip('+'),
                                           'CreateProposalLayer')

    def filter(x):
        return type(x) == cntk.Function and x.op_name == 'UserFunction' and x.name == 'ProposalLayer'

    def converter(x):
        layer_config = copy.deepcopy(x.attributes)
        return cntk.ops.native_user_function('ProposalLayerOp', list(x.inputs), layer_config, 'native_proposal_layer')

    model_w_native_udf = cntk.misc.convert(eval_model, filter, converter)
    model_path = cfg['MODEL_PATH']
    new_model_path = model_path[:-6] + '_native.model'
    model_w_native_udf.save(new_model_path)
    print("Stored eval model with native UDF to {}".format(new_model_path))

def compute_rpn_proposals(rpn_model, image_input, roi_input, dims_input, cfg):
    num_images = cfg["DATA"].NUM_TRAIN_IMAGES
    # Create the minibatch source
    od_minibatch_source = ObjectDetectionMinibatchSource(
        cfg["DATA"].TRAIN_MAP_FILE, cfg["DATA"].TRAIN_ROI_FILE,
        num_classes=cfg["DATA"].NUM_CLASSES,
        max_annotations_per_image=cfg.INPUT_ROIS_PER_IMAGE,
        pad_width=cfg.IMAGE_WIDTH,
        pad_height=cfg.IMAGE_HEIGHT,
        pad_value=cfg["MODEL"].IMG_PAD_COLOR,
        max_images=num_images,
        randomize=False, use_flipping=False,
        proposal_provider=None)

    # define mapping from reader streams to network inputs
    input_map = {
        od_minibatch_source.image_si: image_input,
        od_minibatch_source.roi_si: roi_input,
        od_minibatch_source.dims_si: dims_input
    }

    buffered_proposals = [None for _ in range(num_images)]
    sample_count = 0
    while sample_count < num_images:
        data = od_minibatch_source.next_minibatch(1, input_map=input_map)
        output = rpn_model.eval(data)
        out_dict = dict([(k.name, k) for k in output])
        out_rpn_rois = output[out_dict['rpn_rois']][0]
        buffered_proposals[sample_count] = np.round(out_rpn_rois).astype(np.int16)
        sample_count += 1
        if sample_count % 500 == 0:
            print("Buffered proposals for {} samples".format(sample_count))

    return buffered_proposals

# If a trained model is already available it is loaded an no training will be performed (if MAKE_MODE=True).
def train_faster_rcnn(cfg):
    # Train only if no model exists yet
    model_path = cfg['MODEL_PATH']
    if os.path.exists(model_path) and cfg["CNTK"].MAKE_MODE:
        print("Loading existing model from %s" % model_path)
        eval_model = load_model(model_path)
    else:
        if cfg["CNTK"].TRAIN_E2E:
            eval_model = train_faster_rcnn_e2e(cfg)
        else:
            eval_model = train_faster_rcnn_alternating(cfg)

        eval_model.save(model_path)
        if cfg["CNTK"].DEBUG_OUTPUT:
            plot(eval_model, os.path.join(cfg.OUTPUT_PATH, "graph_frcn_eval_{}_{}.{}"
                                          .format(cfg["MODEL"].BASE_MODEL, "e2e" if cfg["CNTK"].TRAIN_E2E else "4stage", cfg["CNTK"].GRAPH_TYPE)))

        print("Stored eval model at %s" % model_path)
    return eval_model

# Trains a Faster R-CNN model end-to-end
def train_faster_rcnn_e2e(cfg):
    # Input variables denoting features and labeled ground truth rois (as 5-tuples per roi)
    image_input = input_variable(shape=(cfg.NUM_CHANNELS, cfg.IMAGE_HEIGHT, cfg.IMAGE_WIDTH),
                                 dynamic_axes=[Axis.default_batch_axis()],
                                 name=cfg["MODEL"].FEATURE_NODE_NAME)
    roi_input = input_variable((cfg.INPUT_ROIS_PER_IMAGE, 5), dynamic_axes=[Axis.default_batch_axis()])
    dims_input = input_variable((6), dynamic_axes=[Axis.default_batch_axis()])
    dims_node = alias(dims_input, name='dims_input')

    # Instantiate the Faster R-CNN prediction model and loss function
    loss, pred_error = create_faster_rcnn_model(image_input, roi_input, dims_node, cfg)

    if cfg["CNTK"].DEBUG_OUTPUT:
        print("Storing graphs and models to %s." % cfg.OUTPUT_PATH)
        plot(loss, os.path.join(cfg.OUTPUT_PATH, "graph_frcn_train_e2e." + cfg["CNTK"].GRAPH_TYPE))

    # Set learning parameters
    e2e_lr_factor = cfg["MODEL"].E2E_LR_FACTOR
    e2e_lr_per_sample_scaled = [x * e2e_lr_factor for x in cfg["CNTK"].E2E_LR_PER_SAMPLE]
    mm_schedule = momentum_schedule(cfg["CNTK"].MOMENTUM_PER_MB)

    print("Using base model:   {}".format(cfg["MODEL"].BASE_MODEL))
    print("lr_per_sample:      {}".format(e2e_lr_per_sample_scaled))

    train_model(image_input, roi_input, dims_input, loss, pred_error,
                e2e_lr_per_sample_scaled, mm_schedule, cfg["CNTK"].L2_REG_WEIGHT, cfg["CNTK"].E2E_MAX_EPOCHS, cfg)

    return create_faster_rcnn_eval_model(loss, image_input, dims_input, cfg)

# Trains a Faster R-CNN model using 4-stage alternating training
def train_faster_rcnn_alternating(cfg):
    '''
        4-Step Alternating Training scheme from the Faster R-CNN paper:

        # Create initial network, only rpn, without detection network
            # --> train only the rpn (and conv3_1 and up for VGG16)
        # buffer region proposals from rpn
        # Create full network, initialize conv layers with imagenet, use buffered proposals
            # --> train only detection network (and conv3_1 and up for VGG16)
        # Keep conv weights from detection network and fix them
            # --> train only rpn
        # buffer region proposals from rpn
        # Keep conv and rpn weights from step 3 and fix them
            # --> train only detection network
    '''

    # setting pre- and post-nms top N to training values since buffered proposals are used for further training
    test_pre = cfg["TEST"].RPN_PRE_NMS_TOP_N
    test_post = cfg["TEST"].RPN_POST_NMS_TOP_N
    cfg["TEST"].RPN_PRE_NMS_TOP_N = cfg["TRAIN"].RPN_PRE_NMS_TOP_N
    cfg["TEST"].RPN_POST_NMS_TOP_N = cfg["TRAIN"].RPN_POST_NMS_TOP_N

    # Learning parameters
    rpn_lr_factor = cfg["MODEL"].RPN_LR_FACTOR
    rpn_lr_per_sample_scaled = [x * rpn_lr_factor for x in cfg["CNTK"].RPN_LR_PER_SAMPLE]
    frcn_lr_factor = cfg["MODEL"].FRCN_LR_FACTOR
    frcn_lr_per_sample_scaled = [x * frcn_lr_factor for x in cfg["CNTK"].FRCN_LR_PER_SAMPLE]

    l2_reg_weight = cfg["CNTK"].L2_REG_WEIGHT
    mm_schedule = momentum_schedule(cfg["CNTK"].MOMENTUM_PER_MB)
    rpn_epochs = cfg["CNTK"].RPN_EPOCHS
    frcn_epochs = cfg["CNTK"].FRCN_EPOCHS

    feature_node_name = cfg["MODEL"].FEATURE_NODE_NAME
    last_conv_node_name = cfg["MODEL"].LAST_CONV_NODE_NAME
    print("Using base model:   {}".format(cfg["MODEL"].BASE_MODEL))
    print("rpn_lr_per_sample:  {}".format(rpn_lr_per_sample_scaled))
    print("frcn_lr_per_sample: {}".format(frcn_lr_per_sample_scaled))

    debug_output=cfg["CNTK"].DEBUG_OUTPUT
    if debug_output:
        print("Storing graphs and models to %s." % cfg.OUTPUT_PATH)

    # Input variables denoting features, labeled ground truth rois (as 5-tuples per roi) and image dimensions
    image_input = input_variable(shape=(cfg.NUM_CHANNELS, cfg.IMAGE_HEIGHT, cfg.IMAGE_WIDTH),
                                 dynamic_axes=[Axis.default_batch_axis()],
                                 name=feature_node_name)
    feat_norm = image_input - Constant([[[v]] for v in cfg["MODEL"].IMG_PAD_COLOR])
    roi_input = input_variable((cfg.INPUT_ROIS_PER_IMAGE, 5), dynamic_axes=[Axis.default_batch_axis()])
    scaled_gt_boxes = alias(roi_input, name='roi_input')
    dims_input = input_variable((6), dynamic_axes=[Axis.default_batch_axis()])
    dims_node = alias(dims_input, name='dims_input')
    rpn_rois_input = input_variable((cfg["TRAIN"].RPN_POST_NMS_TOP_N, 4), dynamic_axes=[Axis.default_batch_axis()])
    rpn_rois_buf = alias(rpn_rois_input, name='rpn_rois')

    # base image classification model (e.g. VGG16 or AlexNet)
    base_model = load_model(cfg['BASE_MODEL_PATH'])

    print("stage 1a - rpn")
    if True:
        # Create initial network, only rpn, without detection network
            #       initial weights     train?
            # conv: base_model          only conv3_1 and up
            # rpn:  init new            yes
            # frcn: -                   -

        # conv layers
        conv_layers = clone_conv_layers(base_model, cfg)
        conv_out = conv_layers(feat_norm)

        # RPN and losses
        rpn_rois, rpn_losses = create_rpn(conv_out, scaled_gt_boxes, dims_node, cfg)
        stage1_rpn_network = combine([rpn_rois, rpn_losses])

        # train
        if debug_output: plot(stage1_rpn_network, os.path.join(cfg.OUTPUT_PATH, "graph_frcn_train_stage1a_rpn." + cfg["CNTK"].GRAPH_TYPE))
        train_model(image_input, roi_input, dims_input, rpn_losses, rpn_losses,
                    rpn_lr_per_sample_scaled, mm_schedule, l2_reg_weight, rpn_epochs, cfg)

    print("stage 1a - buffering rpn proposals")
    buffered_proposals_s1 = compute_rpn_proposals(stage1_rpn_network, image_input, roi_input, dims_input, cfg)

    print("stage 1b - frcn")
    if True:
        # Create full network, initialize conv layers with imagenet, fix rpn weights
            #       initial weights     train?
            # conv: base_model          only conv3_1 and up
            # rpn:  stage1a rpn model   no --> use buffered proposals
            # frcn: base_model + new    yes

        # conv_layers
        conv_layers = clone_conv_layers(base_model, cfg)
        conv_out = conv_layers(feat_norm)

        # use buffered proposals in target layer
        rois, label_targets, bbox_targets, bbox_inside_weights = \
            create_proposal_target_layer(rpn_rois_buf, scaled_gt_boxes, cfg)

        # Fast RCNN and losses
        fc_layers = clone_model(base_model, [cfg["MODEL"].POOL_NODE_NAME], [cfg["MODEL"].LAST_HIDDEN_NODE_NAME], CloneMethod.clone)
        cls_score, bbox_pred = create_fast_rcnn_predictor(conv_out, rois, fc_layers, cfg)
        detection_losses = create_detection_losses(cls_score, label_targets, bbox_pred, rois, bbox_targets, bbox_inside_weights, cfg)
        pred_error = classification_error(cls_score, label_targets, axis=1, name="pred_error")
        stage1_frcn_network = combine([rois, cls_score, bbox_pred, detection_losses, pred_error])

        # train
        if debug_output: plot(stage1_frcn_network, os.path.join(cfg.OUTPUT_PATH, "graph_frcn_train_stage1b_frcn." + cfg["CNTK"].GRAPH_TYPE))
        train_model(image_input, roi_input, dims_input, detection_losses, pred_error,
                    frcn_lr_per_sample_scaled, mm_schedule, l2_reg_weight, frcn_epochs, cfg,
                    rpn_rois_input=rpn_rois_input, buffered_rpn_proposals=buffered_proposals_s1)
        buffered_proposals_s1 = None

    print("stage 2a - rpn")
    if True:
        # Keep conv weights from detection network and fix them
            #       initial weights     train?
            # conv: stage1b frcn model  no
            # rpn:  stage1a rpn model   yes
            # frcn: -                   -

        # conv_layers
        conv_layers = clone_model(stage1_frcn_network, [feature_node_name], [last_conv_node_name], CloneMethod.freeze)
        conv_out = conv_layers(image_input)

        # RPN and losses
        rpn = clone_model(stage1_rpn_network, [last_conv_node_name, "roi_input", "dims_input"], ["rpn_rois", "rpn_losses"], CloneMethod.clone)
        rpn_net = rpn(conv_out, dims_node, scaled_gt_boxes)
        rpn_rois = rpn_net.outputs[0]
        rpn_losses = rpn_net.outputs[1]
        stage2_rpn_network = combine([rpn_rois, rpn_losses])

        # train
        if debug_output: plot(stage2_rpn_network, os.path.join(cfg.OUTPUT_PATH, "graph_frcn_train_stage2a_rpn." + cfg["CNTK"].GRAPH_TYPE))
        train_model(image_input, roi_input, dims_input, rpn_losses, rpn_losses,
                    rpn_lr_per_sample_scaled, mm_schedule, l2_reg_weight, rpn_epochs, cfg)

    print("stage 2a - buffering rpn proposals")
    buffered_proposals_s2 = compute_rpn_proposals(stage2_rpn_network, image_input, roi_input, dims_input, cfg)

    print("stage 2b - frcn")
    if True:
        # Keep conv and rpn weights from step 3 and fix them
            #       initial weights     train?
            # conv: stage2a rpn model   no
            # rpn:  stage2a rpn model   no --> use buffered proposals
            # frcn: stage1b frcn model  yes                   -

        # conv_layers
        conv_layers = clone_model(stage2_rpn_network, [feature_node_name], [last_conv_node_name], CloneMethod.freeze)
        conv_out = conv_layers(image_input)

        # Fast RCNN and losses
        frcn = clone_model(stage1_frcn_network, [last_conv_node_name, "rpn_rois", "roi_input"],
                           ["cls_score", "bbox_regr", "rpn_target_rois", "detection_losses", "pred_error"], CloneMethod.clone)
        stage2_frcn_network = frcn(conv_out, rpn_rois_buf, scaled_gt_boxes)
        detection_losses = stage2_frcn_network.outputs[3]
        pred_error = stage2_frcn_network.outputs[4]

        # train
        if debug_output: plot(stage2_frcn_network, os.path.join(cfg.OUTPUT_PATH, "graph_frcn_train_stage2b_frcn." + cfg["CNTK"].GRAPH_TYPE))
        train_model(image_input, roi_input, dims_input, detection_losses, pred_error,
                    frcn_lr_per_sample_scaled, mm_schedule, l2_reg_weight, frcn_epochs, cfg,
                    rpn_rois_input=rpn_rois_input, buffered_rpn_proposals=buffered_proposals_s2)
        buffered_proposals_s2 = None

    # resetting config values to original test values
    cfg["TEST"].RPN_PRE_NMS_TOP_N = test_pre
    cfg["TEST"].RPN_POST_NMS_TOP_N = test_post

    return create_faster_rcnn_eval_model(stage2_frcn_network, image_input, dims_input, cfg, rpn_model=stage2_rpn_network)

def train_model(image_input, roi_input, dims_input, loss, pred_error,
                lr_per_sample, mm_schedule, l2_reg_weight, epochs_to_train, cfg,
                rpn_rois_input=None, buffered_rpn_proposals=None):
    if isinstance(loss, cntk.Variable):
        loss = combine([loss])

    params = loss.parameters
    biases = [p for p in params if '.b' in p.name or 'b' == p.name]
    others = [p for p in params if not p in biases]
    bias_lr_mult = cfg["CNTK"].BIAS_LR_MULT

    if cfg["CNTK"].DEBUG_OUTPUT:
        print("biases")
        for p in biases: print(p)
        print("others")
        for p in others: print(p)
        print("bias_lr_mult: {}".format(bias_lr_mult))

    # Instantiate the learners and the trainer object
    lr_schedule = learning_parameter_schedule_per_sample(lr_per_sample)
    learner = momentum_sgd(others, lr_schedule, mm_schedule, l2_regularization_weight=l2_reg_weight,
                           unit_gain=False, use_mean_gradient=True)

    bias_lr_per_sample = [v * bias_lr_mult for v in lr_per_sample]
    bias_lr_schedule = learning_parameter_schedule_per_sample(bias_lr_per_sample)
    bias_learner = momentum_sgd(biases, bias_lr_schedule, mm_schedule, l2_regularization_weight=l2_reg_weight,
                           unit_gain=False, use_mean_gradient=True)
    trainer = Trainer(None, (loss, pred_error), [learner, bias_learner])

    # Get minibatches of images and perform model training
    print("Training model for %s epochs." % epochs_to_train)
    log_number_of_parameters(loss)

    # Create the minibatch source
    if buffered_rpn_proposals is not None:
        proposal_provider = ProposalProvider.fromlist(buffered_rpn_proposals, requires_scaling=False)
    else:
        proposal_provider = None

    od_minibatch_source = ObjectDetectionMinibatchSource(
        cfg["DATA"].TRAIN_MAP_FILE, cfg["DATA"].TRAIN_ROI_FILE,
        num_classes=cfg["DATA"].NUM_CLASSES,
        max_annotations_per_image=cfg.INPUT_ROIS_PER_IMAGE,
        pad_width=cfg.IMAGE_WIDTH,
        pad_height=cfg.IMAGE_HEIGHT,
        pad_value=cfg["MODEL"].IMG_PAD_COLOR,
        randomize=True,
        use_flipping=cfg["TRAIN"].USE_FLIPPED,
        max_images=cfg["DATA"].NUM_TRAIN_IMAGES,
        proposal_provider=proposal_provider)

    # define mapping from reader streams to network inputs
    input_map = {
        od_minibatch_source.image_si: image_input,
        od_minibatch_source.roi_si: roi_input,
    }
    if buffered_rpn_proposals is not None:
        input_map[od_minibatch_source.proposals_si] = rpn_rois_input
    else:
        input_map[od_minibatch_source.dims_si] = dims_input

    progress_printer = ProgressPrinter(tag='Training', num_epochs=epochs_to_train, gen_heartbeat=True)
    for epoch in range(epochs_to_train):       # loop over epochs
        sample_count = 0
        while sample_count < cfg["DATA"].NUM_TRAIN_IMAGES:  # loop over minibatches in the epoch
            data = od_minibatch_source.next_minibatch(min(cfg.MB_SIZE, cfg["DATA"].NUM_TRAIN_IMAGES-sample_count), input_map=input_map)
            trainer.train_minibatch(data)                                    # update model with it
            sample_count += trainer.previous_minibatch_sample_count          # count samples processed so far
            progress_printer.update_with_trainer(trainer, with_metric=True)  # log progress
            if sample_count % 100 == 0:
                print("Processed {} samples".format(sample_count))

        progress_printer.epoch_summary(with_metric=True)


# Creation Date: 08/30/2023 01:13 EDT
# Last Updated Date: 11/04/2023 02:55 PM EDT
# Author: Joseph Armstrong (armstrongjoseph08@gmail.com)
# File Name: drives.py
# Purpose: Houses functions pertaining to CFB drive data within the CFBD API.
####################################################################################################

from datetime import datetime
import logging
import pandas as pd
import requests
from tqdm import tqdm

from cfbd_json_py.utls import get_cfbd_api_token

def get_cfbd_drives_info(
        season: int,
        api_key: str = None,
        api_key_dir: str = None,
        season_type: str = "regular",
        week: int = None,
        team: str = None,
        offensive_team: str = None,
        defensive_team: str = None,
        conference_abv: str = None,
        offensive_conference_abv: str = None,
        defensive_conference_abv: str = None,
        ncaa_division: str = "fbs",
        return_as_dict: bool = False):
    """
    Retrives a list of CFB drives from the CFBD API.

    Parameters
    ----------
    `season` (int, mandatory):
        Required argument.
        Specifies the season you want CFB drive information from.
        This must be specified, otherwise this package, and by extension
        the CFBD API, will not accept the request to get CFB drive information.

    `api_key` (str, optional):
        Semi-optional argument.
        If `api_key` is null, this function will attempt to load a CFBD API key
        from the python environment, or from a file on this computer.
        If `api_key` is not null, this function will automatically assume that the
        inputted `api_key` is a valid CFBD API key.

    `api_key_dir` (str, optional):
        Optional argument.
        If `api_key` is set to am empty string, this variable is ignored.
        If `api_key_dir` is null, and `api_key` is null,
        this function will try to find a CFBD API key file in this user's home directory.
        If `api_key_dir` is set to a string, and `api_key` is null,
        this function will assume that `api_key_dir` is a directory,
        and will try to find a CFBD API key file in that directory.

    `season_type` (str, semi-optional):
        Semi-optional argument.
        By defualt, this will be set to "regular", for the CFB regular season.
        If you want CFB drive data for non-regular season games,
        set `season_type` to "postseason".
        If `season_type` is set to anything but "regular" or "postseason",
        a `ValueError()` will be raised.

    `week` (int, optional):
        Optional argument.
        If `week` is set to an integer, this function will attempt
        to load CFB drive data from games in that season, and that week.

    `team` (str, optional):
        Optional argument.
        If you only want CFB drive data for a team,
        regardless if they are the home/away team,
        set `team` to the name of the team you want CFB drive data from.

    `offensive_team` (str, optional):
        Optional argument.
        If you only want CFB drive data from a team, while they are on offense,
        regardless if they are the home/away team,
        set `team` to the name of the team you want CFB drive data from.

    `defensive_team` (str, optional):
        Optional argument.
        If you only want CFB drive data from a team, while they are on defense,
        regardless if they are the home/away team,
        set `team` to the name of the team you want CFB drive data from.

    `conference_abv` (str, optional):
        Optional argument.
        If you only want CFB drive data from games
        involving teams from a specific confrence,
        set `conference_abv` to the abbreviation
        of the conference you want CFB drive data from.
        For a list of confrences,
        use the `cfbd_json_py.conferences.get_cfbd_conference_info()`
        function.

    `offensive_conference_abv` (str, optional):
        Optional argument.
        If you only want CFB drive data from games
        where the offensive team is from a specific confrenece,
        set `conference_abv` to the abbreviation
        of the conference you want CFB drive data from.
        For a list of confrences,
        use the `cfbd_json_py.conferences.get_cfbd_conference_info()`
        function.

    `defensive_conference_abv` (str, optional):
        Optional argument.
        If you only want CFB drive data from games
        where the defensive team is from a specific confrenece,
        set `conference_abv` to the abbreviation
        of the conference you want CFB drive data from.
        For a list of confrences,
        use the `cfbd_json_py.conferences.get_cfbd_conference_info()`
        function.

    `ncaa_division` (str, semi-optional):
        Semi-optional argument.
        By default, `ncaa_division` will be set to "fbs",
        short for the Football Bowl Subdivision (FBS),
        formerly known as D1-A (read as "division one single A"),
        the highest level in the NCAA football pyramid,
        where teams can scolarship up to 85 players
        on their football team soley for athletic ability,
        and often have the largest athletics budgets
        within the NCAA.

        Other valid inputs are:
        - "fcs": Football Championship Subdivision (FCS),
            formerly known as D1-AA (read as "division one double A").
            An FCS school is still in the 1st division of the NCAA,
            making them elligable for the March Madness tournament,
            but may not have the resources to compete at the FBS level
            at this time. FCS schools are limited to 63 athletic scolarships
            for football.
        - "ii": NCAA Division II. Schools in this and D3 are not
            elligable for the March Madness tournament,
            and are limited to 36 athletic scolarships for their football team.
        - "iii": NCAA Division III. The largest single division within the
            NCAA football pyramid.
            D3 schools have the distinction of being part of
            the only NCAA division that cannot give out scolarships soley
            for athletic ability.

    `return_as_dict` (bool, semi-optional):
        Semi-optional argument.
        If you want this function to return the data as a dictionary (read: JSON object),
        instead of a pandas `DataFrame` object,
        set `return_as_dict` to `True`.

    Usage
    ----------
    ```
    import time

    from cfbd_json_py.drives import get_cfbd_drives_info

    cfbd_key = "tigersAreAwsome"  # placeholder for your CFBD API Key.

    if cfbd_key != "tigersAreAwsome":
        print("Using the user's API key declared in this script for this example.")

        # Get CFB Drive data from the 2020 CFB season.
        print("Get CFB Drive data from the 2020 CFB season.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from week 10 of the 2020 CFB season.
        print("Get CFB Drive data from week 10 of the 2020 CFB season.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            week=10
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from games involving the 2020 Cincinnati Bearcats
        # Football Team.
        print("Get CFB Drive data from games involving the 2020 Cincinnati Bearcats Football Team.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            team="Cincinnati"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from games involving the 2020 Ohio Bobcats
        # Football Team, when Ohio was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio Bobcats Football Team, when Ohio was on offense.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            offensive_team="Ohio"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from games involving the 2020 Ohio State Buckeyes
        # Football Team, when Ohio was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            defensive_team="Ohio State"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from Big 12 games in the 2020 CFB season.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            conference_abv="B12"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from Big 10 (B1G) games in the 2020 CFB season,
        # where the Big 10 team was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            offensive_conference_abv="B1G"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from  Mid-American Conference (MAC) games
        # in the 2020 CFB season, where the MAC team was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            defensive_conference_abv="MAC"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from Football Championship Subdivision (FCS) games
        # in week 3 ofthe 2020 CFB season,
        # where the MAC team was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            api_key=cfbd_key,
            season=2020,
            week=3,
            ncaa_division="fcs"
        )
        print(json_data)
        time.sleep(5)

        # You can also tell this function to just return the API call as
        # a Dictionary (read: JSON) object.
        print("You can also tell this function to just return the API call as a Dictionary (read: JSON) object.")
        json_data = get_cfbd_drives_info(
            season=2020,
            week=10,
            api_key=cfbd_key,
            return_as_dict=True
        )
        print(json_data)

    else:
        # Alternatively, if the CFBD API key exists in this python environment,
        # or it's been set by cfbd_json_py.utls.set_cfbd_api_token(),
        # you could just call these functions directly, without setting the API key
        # in the script.
        print("Using the user's API key suposedly loaded into this python environment for this example.")

        # Get CFB Drive data from the 2020 CFB season.
        print("Get CFB Drive data from the 2020 CFB season.")
        json_data = get_cfbd_drives_info(
            season=2020
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from week 10 of the 2020 CFB season.
        print("Get CFB Drive data from week 10 of the 2020 CFB season.")
        json_data = get_cfbd_drives_info(
            season=2020,
            week=10
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from games involving the 2020 Cincinnati Bearcats
        # Football Team.
        print("Get CFB Drive data from games involving the 2020 Cincinnati Bearcats Football Team.")
        json_data = get_cfbd_drives_info(
            season=2020,
            team="Cincinnati"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from games involving the 2020 Ohio Bobcats
        # Football Team, when Ohio was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio Bobcats Football Team, when Ohio was on offense.")
        json_data = get_cfbd_drives_info(
            season=2020,
            offensive_team="Ohio"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from games involving the 2020 Ohio State Buckeyes
        # Football Team, when Ohio was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            season=2020,
            defensive_team="Ohio State"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from Big 12 games in the 2020 CFB season.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            season=2020,
            conference_abv="B12"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from Big 10 (B1G) games in the 2020 CFB season,
        # where the Big 10 team was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            season=2020,
            offensive_conference_abv="B1G"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from  Mid-American Conference (MAC) games
        # in the 2020 CFB season, where the MAC team was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            season=2020,
            defensive_conference_abv="MAC"
        )
        print(json_data)
        time.sleep(5)

        # Get CFB Drive data from Football Championship Subdivision (FCS) games
        # in week 3 ofthe 2020 CFB season,
        # where the MAC team was on offense.
        print("Get CFB Drive data from games involving the 2020 Ohio State Buckeyes Football Team, when Ohio State was on defense.")
        json_data = get_cfbd_drives_info(
            season=2020,
            week=3,
            ncaa_division="fcs"
        )
        print(json_data)
        time.sleep(5)

        # You can also tell this function to just return the API call as
        # a Dictionary (read: JSON) object.
        print("You can also tell this function to just return the API call as a Dictionary (read: JSON) object.")
        json_data = get_cfbd_drives_info(
            season=2020,
            week=10,
            return_as_dict=True
        )
        print(json_data)

    ```

    Returns
    ----------
    A pandas `DataFrame` object with CFB drive data,
    or (if `return_as_dict` is set to `True`)
    a dictionary object with CFB drive data.

    """
    now = datetime.now()
    cfb_drives_df = pd.DataFrame()
    row_df = pd.DataFrame()
    url = "https://api.collegefootballdata.com/drives"

    # Input validation
    ########################################################################################################################################################################################################

    if api_key != None:
        real_api_key = api_key
        del api_key
    else:
        real_api_key = get_cfbd_api_token(api_key_dir=api_key_dir)

    if real_api_key == "tigersAreAwsome":
        raise ValueError(
            "You actually need to change `cfbd_key` to your CFBD API key.")
    elif "Bearer " in real_api_key:
        pass
    elif "Bearer" in real_api_key:
        real_api_key = real_api_key.replace('Bearer', 'Bearer ')
    else:
        real_api_key = "Bearer " + real_api_key

    if season == None:
        # This should never happen without user tampering, but if it does,
        # we need to raise an error, because the CFBD API will refuse this call without a valid season.
        raise SystemError(
            "I don't know how, I don't know why, but you managed to call this function while `season` was `None` (NULL)," +
            " and the function got to this point in the code." +
            "\nIf you have a GitHub account, please raise an issue on this python package's GitHub page:\n" +
            "https://github.com/armstjc/cfbd-json-py/issues"
        )
    elif season > (now.year + 1):
        raise ValueError(f"`season` cannot be greater than {season}.")
    elif season < 1869:
        raise ValueError(f"`season` cannot be less than 1869.")

    if season_type != "regular" and season_type != "postseason":
        raise ValueError(
            "`season_type` must be set to either \"regular\" or \"postseason\" for this function to work.")

    if ncaa_division.lower() == "fbs" or ncaa_division.lower() == "fcs" \
            or ncaa_division.lower() == "ii" or ncaa_division.lower() == "iii":
        pass
    else:
        raise ValueError(
            "An invalid NCAA Division was inputted when calling this function." +
            "\nValid inputs are:\n-\"fbs\"\n-\"fcs\"\n-\"ii\"\n-\"iii\"" +
            f"\n\nYou entered:\n{ncaa_division}"
        )

    # URL builder
    ########################################################################################################################################################################################################

    # Required by API
    url += f"?seasonType={season_type}"

    url += f"&year={season}"

    if week != None:
        url += f"&week={week}"

    if team != None:
        url += f"&team={team}"

    if offensive_team != None:
        url += f"&offense={offensive_team}"

    if defensive_team != None:
        url += f"&defense={defensive_team}"

    if conference_abv != None:
        url += f"&conference={conference_abv}"

    if offensive_conference_abv != None:
        url += f"&offenseConference={offensive_conference_abv}"

    if defensive_conference_abv != None:
        url += f"&defenseConference={defensive_conference_abv}"

    if ncaa_division != None:
        url += f"&classification={ncaa_division.lower}"

    headers = {
        'Authorization': f'{real_api_key}',
        'accept': 'application/json'
    }

    response = requests.get(url, headers=headers)

    if response.status_code == 200:
        pass
    elif response.status_code == 401:
        raise ConnectionRefusedError(
            f'Could not connect. The connection was refused.\nHTTP Status Code 401.'
        )
    else:
        raise ConnectionError(
            f'Could not connect.\nHTTP Status code {response.status_code}'
        )

    json_data = response.json()

    if return_as_dict == True:
        return json_data

    for drive in tqdm(json_data):
        offense = drive['offense']
        row_df = pd.DataFrame(
            {
                "offense": offense
            }, index=[0]
        )
        del offense

        row_df['offense_conference'] = drive['offense_conference']
        row_df['defense'] = drive['defense']
        row_df['defense_conference'] = drive['defense_conference']
        row_df['game_id'] = drive['game_id']
        row_df['drive_id'] = drive['id']
        row_df['drive_number'] = drive['drive_number']
        row_df['is_scoring_drive'] = drive['scoring']
        row_df['drive_start_period'] = drive['start_period']
        row_df['drive_start_yardline'] = drive['start_yardline']
        row_df['drive_start_yards_to_goal'] = drive['start_yards_to_goal']
        row_df['drive_start_time_minutes'] = drive['start_time']['minutes']
        row_df['drive_start_time_seconds'] = drive['start_time']['seconds']
        row_df['drive_end_period'] = drive['end_period']
        row_df['drive_end_yardline'] = drive['end_yardline']
        row_df['drive_end_yards_to_goal'] = drive['end_yards_to_goal']
        row_df['drive_end_time_minutes'] = drive['end_time']['minutes']
        row_df['drive_end_time_seconds'] = drive['end_time']['seconds']
        row_df['drive_elapsed_minutes'] = drive['elapsed']['minutes']
        row_df['drive_elapsed_seconds'] = drive['elapsed']['seconds']
        row_df['drive_plays'] = drive['plays']
        row_df['drive_yards'] = drive['yards']
        row_df['drive_result'] = drive['drive_result']
        row_df['is_home_offense'] = drive['is_home_offense']
        row_df['start_offense_score'] = drive['start_offense_score']
        row_df['start_defense_score'] = drive['start_defense_score']
        row_df['end_offense_score'] = drive['end_offense_score']
        row_df['end_defense_score'] = drive['end_defense_score']

        cfb_drives_df = pd.concat([cfb_drives_df, row_df], ignore_index=True)

        del row_df

    if len(cfb_drives_df) == 0:
        logging.error(
            "The CFBD API accepted your inputs, " +
            "but found no data within your specified input paramaters." +
            " Please double check your input paramaters."
        )

    return cfb_drives_df


"""
Airflow Docs: https://airflow.apache.org/docs/apache-airflow-providers-google/stable/operators/cloud/index.html
"""

from airflow.providers.google.cloud.hooks.gcs import GCSHook

TENNIS_DIRECTORY = 'atp_tennis_data'
PLAYERS_TABLE = f'{TENNIS_DIRECTORY}/atp_players'
MATCHES_TABLE = f'{TENNIS_DIRECTORY}/atp_matches'
RANKINGS_TABLE = f'{TENNIS_DIRECTORY}/atp_rankings'

gcs_hook = GCSHook(
    gcp_conn_id="google_cloud_default",
    delegate_to=None,
    impersonation_chain=None,
)
def run_gcs_task(destination_bucket, prefix, pq_file, local_file):
    """run gcs task to upload files to gcs

    Args:
        destination_bucket (_type_): destination bucket
        prefix (_type_): the prefix/folder for the file name
        pq_file (_type_): the parquet file name
        local_file (_type_): the local file name
    """
    gcs_hook.upload(
            bucket_name=destination_bucket,
            object_name=f'{prefix}/{pq_file}',
            filename=local_file,
            mime_type='application/octet-stream',
            gzip=False,
    )

def upload_players_to_gcs(FILE_PATH, destination_bucket):
    """upload players to gcs

    Args:
        FILE_PATH (_type_): the path to the data files
        destination_bucket (_type_): the destination bucket
    """
    print('Uploading players to GCS')
    run_gcs_task(destination_bucket, PLAYERS_TABLE, 'atp_players.parquet', f'{FILE_PATH}/players/atp_players_transformed.parquet')
    print('Players uploaded to GCS')

def upload_matches_to_gcs(FILE_PATH, destination_bucket, MATCH_YEARS):
    """upload matches to gcs

    Args:
        FILE_PATH (_type_): the path to the data files
        destination_bucket (_type_): the destination bucket
        MATCH_YEARS (_type_): a list of years to load
    """
    print(f'Uploading matches to GCS')
    for match in MATCH_YEARS:
        run_gcs_task(destination_bucket, MATCHES_TABLE, f'atp_matches_{match}.parquet', f'{FILE_PATH}/matches/atp_matches_{match}_transformed.parquet')
    print(f'Matches uploaded to GCS')

def upload_rankings_to_gcs(FILE_PATH, destination_bucket, RANKING_YEARS):
    """ upload rankings to gcs

    Args:
        FILE_PATH (_type_): the path to the data files
        destination_bucket (_type_): the destination bucket
        RANKING_YEARS (_type_): a list of years to load
    """
    print(f'Uploading rankings to GCS')
    for rank in RANKING_YEARS:
        run_gcs_task(destination_bucket, RANKINGS_TABLE, f'atp_rankings_{rank}.parquet', f'{FILE_PATH}/rankings/atp_rankings_{rank}_transformed.parquet')
    print(f'Rankings uploaded to GCS')


"""
Dataset Utils

Created by: @SteveyyP

"""

# Import Libraries
import numpy as np
import os
import cv2
import matplotlib.pyplot as plt
import pandas as pd
import pickle
import tensorflow as tf
from tqdm import tqdm_notebook
from scipy.spatial.distance import hamming, cosine

# Image Loader
def image_loader(image_path, size):
  """
  Load Image from Path

  Inputs
  image_path - string path to image file
  size - tuple of size of image (w, h)

  Output
  image - color and size corrected image
  """
  image = cv2.imread(image_path)
  image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
  image = cv2.resize(image, (size), cv2.INTER_CUBIC)
  return image

# Dataset Preprocessing
def dataset_preprocessing(dataset_path, labels_file_path, size, image_path_pickle):
  """
  Load Images and labels from folder

  Inputs
  dataset_path - folder location of dataset
  labels_file_path - file location of labels
  size - tuple of size of image (w, h)
  image_path_pickle - file write location of image pickle file

  Output
  images - numpy array of images
  labels - numpy array of labels
  """

  with open(labels_file_path, 'r') as f:
    classes = f.read().split('\n')[:-1]

  images = []
  labels = []
  image_paths = []

  for image_name in os.listdir(dataset_path):
    try:
      image_path = os.path.join(dataset_path, image_name)
      images.append(image_loader(image_path, size))
      image_paths.append(image_path)
      for i in range(len(classes)):
        if classes[i] in image_name:
          labels.append(i)
    except:
      pass

  with open(image_path_pickle + ".pickle", 'wb') as f:
    pickle.dump(image_paths, f)

  assert len(images) == len(labels)
  return np.array(images), np.array(labels)


import unittest
import pandas as pd
from sklearn import metrics

from modelo.configuracoes.config import AppConfig
from modelo.preve_modelo import preve_pipeline
from modelo.processing.gerenciamendo_dados import carrega_dataset
from modelo.configuracoes.caminhos import DADOS_DIR

class TestFunc(unittest.TestCase):

    def test_prev_precisao(self):

        #Teste para verificar se a precisão dos dados previstos se mantem com a
        #implementação do modelo

        #carrega os dados
        str_file=DADOS_DIR/AppConfig.nome_dados_teste
        df = carrega_dataset(str_file)

        #faz previsao
        results = preve_pipeline(df)
        y_hat = results.get("predictions")

        # transformação: dados previstos
        class_map = {0:'C', 1:'D', 2:'A', 3:'B'}
        y_hat = pd.Series(y_hat).map(class_map)

        #precisao
        prec=metrics.accuracy_score(df['class'], y_hat)

        #estatisticas com o modelo
        self.assertEqual(prec,0.7139303482587065,"A precisão não é a mesma do desenvolvimento")

        #OBS: a prcisao do modelo não é a colocada acima.
        #acho que isto aocnteeu devido a validadacao dos
        #dados pelo pydantic

    def test_prev_valores(self):

        #Teste para verificar se os valores para algumas previsões estão corretos

        #carrega os dados
        str_file=DADOS_DIR/AppConfig.nome_dados_teste
        df = carrega_dataset(str_file)

        results = preve_pipeline(df)
        y_hat = results.get("predictions")
        y_hat = pd.Series(y_hat).map({0:'C', 1:'D', 2:'A', 3:'B'})

        #OBS: segue abaixo como era feita a separação dos dados antes.Atualmente
        #a validação dos dados é feita dentro da função 'preve_pipeline' e a
        #e com a validação também são feitas as seleções
        #var_prev=[var for var in df.columns if var not in ['Unnamed: 0','class','prev_val']]
        #X = df[var_prev]

        #verifica para 10 valores
        for i in range(10):
            self.assertEqual(y_hat[i], df['prev_val'][i], "Valor da previsao não bateu")

# ponto de entrada
if __name__=="__main__":
    unittest.main()


from flask import Flask
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy

bootstrap=Bootstrap()
db=SQLAlchemy()

def create_app():

    app=Flask(__name__)

    app.config['SECRET_KEY'] = '123456'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:1205@127.0.0.1:3306/Air'
    app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

    db = SQLAlchemy(app)

    db.init_app(app)
    bootstrap.init_app(app)

    from .AirViz import airviz as airviz_blueprint
    app.register_blueprint(airviz_blueprint)

    return app


from flask import Flask
from .views import main

class CustomFlask(Flask):
    jinja_options = Flask.jinja_options.copy()
    jinja_options.update(dict(
        variable_start_string='{$',  # Default is '{{', I'm changing this because Vue.js uses '{{' / '}}'
        variable_end_string='$}',
    ))

app = CustomFlask(__name__)
app.register_blueprint(main, url_prefix='/')


from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.conf import settings
from django.contrib import messages
from smtplib import SMTPException
import secrets
from datetime import datetime

from .models import PersonToken
import logging

logger = logging.getLogger(__name__)

AUTH_TOKEN_LENGTH = getattr(settings, 'AUTH_TOKEN_LENGTH', 6)
AUTH_TOKEN_VALID_MINUTES = getattr(settings, 'AUTH_TOKEN_VALID_MINUTES', 30)
AUTH_EMAIL_RETRY_SECONDS = getattr(settings, 'AUTH_EMAIL_RETRY_SECONDS', 10)

CODE_CHARS = '0123456789'
def get_random_code(length):
    return ''.join(secrets.choice(CODE_CHARS) for _ in range(length))

def send_account_auth_email(request, name, email):
    """ Generate random token, store token in session, and send email - with timeout to prevent accidental spam """
    prev_time = int(request.session.get('auth_token_timestamp', 0))
    now = int(datetime.now().timestamp())
    if now < prev_time + 10:
        messages.warning(request, 'Cannot send auth code emails more than once every %d seconds' % AUTH_EMAIL_RETRY_SECONDS)
        return False

    token = make_auth_token(request)

    ctx = {
        'name': name,
        'auth_code': token,
        'valid_minutes': AUTH_TOKEN_VALID_MINUTES,
        'site_url': getattr(settings, 'SITE_BASE_URL'),
    }
    try:
        ok = send_mail(
            'Your Peddle Riders Code is: %s' % token,
            render_to_string('email/email_auth.txt', ctx),
            getattr(settings, 'DEFAULT_FROM_EMAIL'),
            [email],
            html_message = render_to_string('email/email_auth.html', ctx),
        )

        messages.success(request, 'Authentication code sent. Please check your inbox.')
    except SMTPException as e:
        logger.error('Error sending email: %s: %s' % (type(e), e))
        messages.error(request,
        'Error sending auth code email. Check your details are correct and try again. (%s)' % (
            type(e).__name__))
        ok = False

    return bool(ok)

def make_auth_token(request):
    token = get_random_code(AUTH_TOKEN_LENGTH)
    now = int(datetime.now().timestamp())
    request.session['auth_token'] = token
    request.session['auth_token_timestamp'] = str(now)
    request.session['auth_token_attempts'] = 0
    return token

def validate_auth_token(request, token):
    """ Validate token for a particular request """
    if not ('auth_token' in request.session and 'auth_token_timestamp' in request.session):
        return False

    now = int(datetime.now().timestamp())
    sess_token = request.session['auth_token']
    token_timestamp = int(request.session['auth_token_timestamp'])
    num_attempts = int(request.session['auth_token_attempts'])

    if sess_token == token and now < token_timestamp + AUTH_TOKEN_VALID_MINUTES * 60 and num_attempts < 10:
        del request.session['auth_token']
        del request.session['auth_token_timestamp']
        del request.session['auth_token_attempts']
        return True

    return False

def send_payroll_change_email(person):
    ctx = {
        'name': person.name,
        'site_url': getattr(settings, 'SITE_BASE_URL'),
    }
    try:
        ok = send_mail(
            'Peddle payroll details updated',
            render_to_string('email/payroll_change.txt', ctx),
            getattr(settings, 'DEFAULT_FROM_EMAIL'),
            [person.email],
            html_message = render_to_string('email/payroll_change.html', ctx),
        )
    except SMTPException as e:
        logger.error('Error sending email: %s: %s' % (type(e), e))
        ok = False

    return bool(ok)


"""A program that encodes and decodes hidden messages in images through LSB steganography"""
from PIL import Image, ImageFont, ImageDraw
import textwrap
import numpy as np

def decode_image(file_location="images/encoded_sample.png", final_location="images/decoded_image.png"):
    """Decodes the hidden message in an image.

    Parameters
    ----------
    file_location: str
        The location of the image file to decode. This defaults to the provided
        encoded image in the images folder.
    """
    encoded_image = Image.open(file_location)

    x_size = encoded_image.size[0]
    y_size = encoded_image.size[1]

    decoded_image = Image.new("RGB", encoded_image.size)

    for x in range(x_size):
        for y in range(y_size):
            pixel = encoded_image.getpixel((x,y))[0]
            binary = bin(pixel)
            if binary[-1:] == "0":
                decoded_image.putpixel((x,y), (0,0,0))
            elif binary[-1:] == "1":
                decoded_image.putpixel((x,y), (255,255,255))

    decoded_image.save(final_location)

def write_text(text_to_write, image_size):
    """Write text to an RGB image. Automatically line wraps.

    Parameters
    ----------
    text_to_write: str
        The text to write to the image.
    image_size: (int, int)
        The size of the resulting text image.
    """
    image_text = Image.new("RGB", image_size)
    font = ImageFont.load_default().font
    drawer = ImageDraw.Draw(image_text)

    # Text wrapping. Change parameters for different text formatting
    margin = offset = 10
    for line in textwrap.wrap(text_to_write, width=60):
        drawer.text((margin, offset), line, font=font)
        offset += 10
    image_text.save("images/image_text.png")
    return image_text

def encode_image(text_to_encode, template_image="images/samoyed.jpg"):
    """Encode a text message into an image.

    Parameters
    ----------
    text_to_encode: str
        The text to encode into the template image.
    template_image: str
        The image to use for encoding. An image is provided by default.
    """
    template_image = Image.open(template_image)
    x_size = template_image.size[0]
    y_size = template_image.size[1]

    write_text(text_to_encode, (x_size, y_size))
    text_image = Image.open("images/image_text.png")

    encoded_image = Image.new("RGB", template_image.size)

    for x in range(x_size):
        for y in range(y_size):
            temp_pixel = template_image.getpixel((x,y))
            text_pixel = text_image.getpixel((x,y))

            red = temp_pixel[0]
            green = temp_pixel[1]
            blue  = temp_pixel[2]
            og_binary = bin(red)

            if text_pixel == (255,255,255):
                new_binary = og_binary[:-1]
                new_binary += "1"
            elif text_pixel == (0,0,0):
                new_binary = og_binary[:-1]
                new_binary += "0"
            new_red = int(new_binary, 2)
            new_pixel = (new_red, green, blue)
            encoded_image.putpixel((x, y), new_pixel)

    encoded_image.save("images/myencoding.png")

if __name__ == '__main__':
    print("Decoding the image...")
    decode_image()

    print("Encoding the image...")
    encode_image("The story so far: In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move." +
                 "                                                                                " +
                 "                                                                                " +
                 "'The Answer to the Ultimate Question of Life, The Universe, and Everything is...Forty-two,' said Deep Thought, with infinite majesty and calm."+
                 "                                                                                " +
                 "                                                                                " +
                 " It is a well known fact that those people who most want to rule people are, ipso facto, those least suited to do it. To summarize the summary: anyone who is capable of getting themselves made President should on no account be allowed to do the job."+
                 "                                                                                " +
                 "                                                                                " +
                 "There is an art, it says, or rather, a knack to flying. The knack lies in learning how to throw yourself at the ground and miss." +
                 "                                                                                " +
                 "                                                                                " +
                 "Nothing travels faster than the speed of light with the possible exception of bad news, which obeys its own special laws." +
                 "                                                                                " +
                 "                                                                                " +
                 "In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move." +
                 "                                                                                " +
                 "                                                                                " +
                 "Don't Panic" +
                 "                                                                                " +
                 "                                                                                " +
                 "                                                                                " +
                 "- The Ultimate Hitchhiker's Guide to the Galaxy by Douglas Adams")

    print("Decoding your own image!")
    decode_image("images/myencoding.png", "images/mydecoding.png")


from typing import List

def insert(intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
    res, n = [], newInterval
    for index, i in enumerate(intervals):
        if i[1] < n[0]:
            res.append(i)
        elif n[1] < i[0]:
            res.append(n)
            return res+intervals[index:]  # can return earlier
        else:  # overlap case
            n[0] = min(n[0], i[0])
            n[1] = max(n[1], i[1])
    res.append(n)
    return res


from molecule import Molecule
import qm, mqc
from thermostat import *
from misc import data

geom = """
2
He2
He   -0.8   0.0   0.0   0.0 0.0 0.0
He    0.8   0.0   0.0   0.0 0.0 0.0
"""

mol = Molecule(geometry=geom, nstates=2)

qm = qm.gaussian09.DFT(molecule=mol, nthreads=4, memory="4gb", functional="B3LYP", basis_set="aug-cc-pVTZ", \
    g09_root_path="/opt/gaussian/")

md = mqc.BOMD(molecule=mol, nsteps=500, dt=0.125, istate=1)

bathT = rescale1(temperature=300.0, nrescale=10)

md.run(molecule=mol, qm=qm, thermostat=bathT, input_dir="./TRAJ.He2", save_scr=True, save_QMlog=False)


from on.models import Activity, Goal, RunningGoal, SleepingGoal, ReadingGoal, RunningPunchRecord, ReadingPunchRecord
from on.activities.reading.models import Read_Finish_Save
import decimal
from datetime import timedelta
from logging import getLogger
import requests
from django.conf import settings
from django.http import JsonResponse, HttpResponseNotFound
from django.shortcuts import render
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt
from wechatpy.utils import random_string
from on.activities.reading.models import ReadTime, BookInfo
from on.models import ReadingGoal,ReadingPunchRecord
from on.settings.local import DEBUG
from on.task import send_img
from on.temp.push_template import do_push
from on.user import UserTicket, UserRecord, UserInfo, UserOrder

def finish_tem(openid, url, activate, finish_time, earn_money, earn_time, balance):
    data = {
        "touser": openid,
        "template_id": "AGL7ztiilHdjd4-5UDIscJOMlUdUGWr2gXvRJiy_ulQ",
        "url": url,
        "topcolor": "#FF0000",
        "data": {
            "first": {
                "value": "您好，有一笔活动收入结算成功",
                "color": "#173177"
            },
            "keyword1": {
                "value": activate,
                "color": "#173177"
            },
            "keyword2": {
                "value": finish_time,
                "color": "#173177"
            },
            "keyword3": {
                "value": earn_money,
                "color": "#173177"
            },
            "keyword4": {
                "value": earn_time,
                "color": "#173177"
            },
            "keyword5": {
                "value": balance,
                "color": "#173177"
            },
            "remark": {
                "value": "点击详情，查看个人中心",
                "color": "#173177"
            },
        }
    }
    return data

@csrf_exempt
def delete_read_goal(request):
    try:
        user = request.session['user']
        if request.POST:
            activity_type = request.POST['goal_type']
            goal_id = request.POST['goal']
            if not all([activity_type, goal_id]):
                return JsonResponse({"status": 403})
            print("参数完整")
            read = ReadingGoal.objects.get(goal_id=goal_id)
            UserRecord.objects.finish_goal(user=user)
            # 更新用户的押金
            print("开始更新押金")
            UserInfo.objects.update_deposit(user_id=user.user_id,
                                            pay_delta=-(read.guaranty - read.bonus))
            print("更新成功")
            UserInfo.objects.read_handle(user_id=user.user_id, bonus=read.bonus)
            print("更新用户的余额成功")
            try:
                print("开始保存用户的数据")
                Read_Finish_Save.objects.save_finish(goal_id=goal_id)
                import time
                time.sleep(1)
            except Exception as e:
                print("记录用户的结束保存信息失败", e)
            try:
                ReadingPunchRecord.objects.filter(goal_id=goal_id).delete()
                ReadingGoal.objects.filter(goal_id=goal_id).delete()
            except Exception as e:
                print("删除读书活动失败", e)
            try:
                read.update_activity_person()
                act = Activity.objects.get(activity_id="fac28454e818458f86639e7d40554597")
                act.bonus_all -= read.guaranty
                act.save()
            except Exception as e:
                print("更新失败", e)
            # 更新用户的余额
            try:
                url = 'http://wechat.onmytarget.cn/user/index'
                activate = "阅读"
                finish_time = timezone.now().strftime('%Y-%m-%d %H:%M')
                earn_money = str(read.bonus)
                earn_time = timezone.now().strftime('%Y-%m-%d %H:%M')
                # 发送模板提醒
                data = finish_tem(user.wechat_id, url, activate, finish_time, earn_money, earn_time, str(user.balance))
                do_push(data)
                return JsonResponse({'status': 200})
            except Exception as e:
                print("发送读书模板失败", e)
    except Exception as e:
        print("读书结束失败", e)
        return JsonResponse({"status": 403})

@csrf_exempt
def create_read(request):
    user = request.session["user"]
    if request.POST:
        print("开始创建读书活动")
        maxreturn = request.POST["maxreturn"]
        bookname = request.POST["bookname"]
        goalpage = request.POST["goalpage"]
        bookprice = request.POST["bookprice"]
        imageurl = request.POST["imageurl"]
        guaranty = request.POST["guaranty"]

        reality_price = request.POST["reality_price"]
        deserve_price = request.POST["deserve_price"]
        if not all([maxreturn, bookname, goalpage, bookprice, imageurl, guaranty, reality_price,
                    deserve_price]):
            print("创建图书活动的参数不全")
            return JsonResponse({"status": 201})
        try:
            goal = ReadingGoal.objects.create_goal(user_id=user.user_id,
                                                   guaranty=guaranty,
                                                   max_return=maxreturn,
                                                   book_name=bookname,
                                                   goal_page=goalpage,
                                                   price=bookprice,
                                                   imageurl=imageurl,
                                                   reality_price=reality_price,
                                                   deserve_price=deserve_price
                                                   )
            print("创建目标成功{}".format(user.user_id))
            response_data = {'status': 200, 'goal': goal.goal_id}
            return JsonResponse(response_data)
        except Exception as e:
            print("读书目标或订单创建失败", e)
    else:
        return HttpResponseNotFound

# 开启书籍阅读
def reading_start_handler(request):
    if request.POST:
        user = request.session.get("user")
        goal_id = request.POST.get("goal")
        try:
            ReadingGoal.objects.filter(goal_id=goal_id).update(is_start=1, start_time=timezone.now())
            UserOrder.objects.filter(goal_id=goal_id).update(confirm_time=timezone.now())
            return JsonResponse({"status": 200})
        except Exception as e:
            print("没有保存成功{}".format(e))
            return JsonResponse({'status': 201})
    else:
        return HttpResponseNotFound

# 书籍阅读打卡
@csrf_exempt
def reading_record_handler(request):
    if request.POST:
        user = request.session['user']
        try:
            read = ReadTime.objects.filter(user_id=user.user_id)[0]
            time_range = (timezone.now() - read.start_read).seconds
            read.time_range = time_range
            read.is_reading = 0
            read.save()
            book_id = request.POST.get("book_id")
            # 本次阅读了page页，耗时time秒
            goal_id = request.POST['goal']
            # timedelta = request.POST['time']
            page = request.POST['page']
            if not all([goal_id, page, book_id]):
                return JsonResponse({"status": 201, "errmsg": "参数不完整"})
            punch_id = ReadingPunchRecord.objects.filter(record_time=timezone.now().strftime("%Y-%m-%d"),
                                                         user_id=user.user_id)
            book = BookInfo.objects.get(book_id=book_id)
            suggest_day = book.suggest_day
            page_num = book.page_num
            guaranty = book.guaranty
            # 每页的金额数
            page_avg = decimal.Decimal(guaranty / page_num)
            record_time = timezone.now().strftime("%Y-%m-%d")
            # 新建一个阅读记录，并返回返回值
            punch = ReadingPunchRecord.objects.create_record(goal_id=goal_id,
                                                             today_page=page,
                                                             today_time=time_range,
                                                             page_avg=page_avg,
                                                             record_time=record_time,
                                                             punch_id=punch_id,
                                                             user_id=user.user_id,
                                                             suggest_day=suggest_day)
            print(punch, "当前的读书系数")
            return JsonResponse({"status": 200, "punch": punch})
        except Exception as e:
            print(e)
    else:
        return HttpResponseNotFound

# 完成阅读
def finish_read(request):
    user = request.session.get("user")
    if request.POST:
        read = ReadTime.objects.filter(user_id=user.user_id)[0]
        try:
            time_range = (timezone.now() - read.start_read).seconds
            print(type(time_range))
            read.time_range = time_range
            read.save()
            return JsonResponse({"status": 200, "time_range": time_range})
        except Exception as e:
            print(e)
    else:
        return HttpResponseNotFound

# 用户主动结束
def success_read(request):
    user = request.session.get("user")
    if request.POST:
        try:
            goal_id = request.POST.get("goal")
            ReadingGoal.objects.filter(goal_id=goal_id, user_id=user.user_id).update(status="SUCCESS")
            return JsonResponse({"status": 200})
        except Exception as e:
            print("未结束成功", e)
    else:
        return HttpResponseNotFound

# 放弃阅读
@csrf_exempt
def give_up_read(request):
    user = request.session.get("user")
    if request.POST:
        try:
            ReadTime.objects.filter(user_id=user.user_id).update(start_read=timezone.now(), is_reading=0)
            return JsonResponse({"status": 200})
        except Exception as e:
            print("放弃阅读失败", e)
            return JsonResponse({"status": 403})
    else:
        return JsonResponse({"status": 403})

@csrf_exempt
def read_prise(request):
    from on.activities.reading.models import Comments
    if request.POST:
        user = request.session.get("user")
        id = request.POST.get("id")
        try:
            Comments.objects.praise_comment(user_id=user.user_id, punch_id=id)
            return JsonResponse({"status": 200})
        except Exception as e:
            print("点赞失败", e)
            return JsonResponse({"status": 401})
    else:
        return JsonResponse({"status": 401})

@csrf_exempt
def read_report(request):
    from on.activities.reading.models import Comments

    user = request.session.get("user")
    if request.POST:
        id = request.POST.get("id")
        try:
            Comments.objects.report_comment(user_id=user.user_id, punch_id=id)
            return JsonResponse({"status": 200})
        except Exception as e:
            print("举报失败", e)
            return JsonResponse({"status": 401})
    else:
        return JsonResponse({"status": 401})


import pygame

from src.animation import Animation
from src.common import EventInfo, Position
from src.enums import EntityStates
from src.utils import reverse_animation

class Cat:
    def __init__(self, assets: dict):
        self.screen_size = pygame.display.get_window_size()
        self.animations = {
            "walk_left": Animation(assets["cat_walk"], 0.05),
            "idle_left": Animation(assets["cat_idle"], 0.005),
            "jump_left": Animation(assets["cat_jump"], 1),
        }
        self.animations |= {
            "walk_right": reverse_animation(self.animations["walk_left"]),
            "idle_right": reverse_animation(self.animations["idle_left"]),
            "jump_right": reverse_animation(self.animations["jump_left"]),
        }
        self.pos = pygame.Vector2()
        self.vel = pygame.Vector2(1)
        self.gravity = 0.25
        self.rect = self.animations["jump_left"].frames[0].get_rect()

        self.state = EntityStates.WALK
        self.facing = "right"

    def move_dnd(self, event_info: EventInfo):
        """Drag-and-drop cat movement"""
        self.rect.center = event_info["mouse_pos"]
        self.pos.xy = self.rect.topleft
        self.vel.y = max(event_info["mouse_vel"].y, -8)
        self.vel.x = max(min(event_info["mouse_vel"].x, 20), -20)

    def move(self, event_info: EventInfo):
        dt = event_info["dt"]
        self.pos += self.vel * dt
        self.rect.topleft = self.pos
        self.vel.y += self.gravity * dt

        if self.vel.x not in (-1, 1):
            self.vel.x = round(pygame.Vector2(self.vel.x).move_towards((1, 0), round(1 * dt)).x)

        if self.vel.x > 0:
            self.facing = "right"
        elif self.vel.x < 0:
            self.facing = "left"

    def update(self, event_info: EventInfo):
        if event_info["mouse_btn"][0]:
            self.move_dnd(event_info)
        else:
            self.move(event_info)

        if self.rect.bottom >= self.screen_size[1]:
            self.vel.y = 0
            self.rect.bottom = self.screen_size[1]
            self.pos.y = self.rect.top
        elif self.rect.top < 0:
            self.vel.y = 0
            self.rect.top = 0
            self.pos.y = self.rect.top

        if self.rect.left <= 0:
            self.vel.x *= -1
            self.rect.left = 0
            self.pos.x = self.rect.x
        elif self.rect.right >= self.screen_size[0]:
            self.vel.x *= -1
            self.rect.right = self.screen_size[0]
            self.pos.x = self.rect.x

    def draw(self, screen: pygame.Surface, event_info: EventInfo):
        self.animations[f"{self.state.value}_{self.facing}"].play(
            screen, self.pos, event_info["dt"]
        )