#!/usr/bin/python
# -*- coding: utf-8 -*-
# utf-8 中文编码
import sys
from collections import OrderedDict

class ExitWithError(Exception):
    pass

class MultipleWith:
    def __init__(self, *args, **kwargs):
        u"""
        可以接受的参数格式：

        # 可以保证顺序，前面的处于外侧，后面的处于内测。
        with multiplewith(open('/path1','wb'),open('/path2','wb')) as file_list:
            file_list[1].write(file_list[0].read())

        # 由于 python dict 限制，无法保证顺序。
        with multiplewith(file1=open('/path1','wb'),file2=open('/path2','wb')) as file_dict:
            file_dict['file1'].write(file_dict.file2.read())

        ordered_dict = OrderedDict((
                                    ('A', 1),
                                    ('B', 2),
                                    ('C', 3)
                                    ))
        # OrderedDict 的用法，可保证顺序。
        with multiplewith(ordered_dict.items()):
            file_dict['file1'].write(file_dict.file2.read())

        # 效果同上，可以保证顺序
        with multiplewith(('file1',open('/path1','wb')),('file2',open('/path2','wb'))):
            file_dict['file1'].write(file_dict.file2.read())
        """
        if args and not kwargs:
            if isinstance(args[0], (list, tuple)):
                kwargs = OrderedDict(args)
                args = None
            else:
                self._contexts = []
                for c in args:
                    self._contexts.append(c)
                return
        if not args and kwargs:
            if isinstance(kwargs, OrderedDict):
                self._contexts = kwargs
            else:
                self._contexts = OrderedDict(kwargs)
        else:
            raise ValueError()

    def __enter__(self):
        u""" 进入 with 块 """
        self.__exit_list = []
        exc_type, exc_val, exc_tb = None, None, None

        if isinstance(self._contexts, (list, tuple)):
            # list 类型
            res = []
            for c in self._contexts:
                try:
                    t = c.__enter__()
                except:
                    # 如果出现异常，那么反向调用 __exit__
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    self.__exit(exc_type, exc_value, exc_traceback, True)
                    # TODO: 正常 with嵌套这里应该是直接退出 with 块，并且异常被捕获并抛弃了。
                    # TODO: 但是目前没有想到什么办法可以不引发异常退出 with 块。
                    raise ExitWithError()

                self.__exit_list.append(c)
                res.append(t)
            return res

        elif isinstance(self._contexts, (dict, OrderedDict)):
            # dict
            res = {}
            for k, v in self._contexts.items():
                try:
                    t = v.__enter__()
                except:
                    exc_type, exc_value, exc_traceback = sys.exc_info()
                    self.__exit(exc_type, exc_value, exc_traceback, True)
                    # TODO: 正常 with嵌套这里应该是直接退出 with 块，并且异常被捕获并抛弃了。
                    # TODO: 但是目前没有想到什么办法可以不引发异常退出 with 块。
                    raise ExitWithError()

                self.__exit_list.append(v)
                res[k] = t
            return res

    def __exit__(self, exc_type, exc_val, exc_tb):
        return self.__exit(exc_type, exc_val, exc_tb, False)

    def __exit(self, exc_type, exc_val, exc_tb, is_raise):
        u""" 反向 exit

    is_raise 是否抛出异常(如果是 __exit__ 引发的异常，会强制抛出)
          """
        #  python 2 exit 抛出异常会被丢弃，python 3 会正常抛出with块。
        self.__exit_list.reverse()

        for _exit in self.__exit_list:
            res = False
            try:
                res = _exit.__exit__(exc_type, exc_val, exc_tb)
            except:
                is_raise = True
                _exc_type, _exc_val, _exc_traceback = sys.exc_info()
                _exc_val.__context__ = exc_val

                exc_type, exc_val, exc_tb = _exc_type, _exc_val, _exc_traceback
            if res:
                exc_type, exc_val, exc_tb = None, None, None

        if exc_val:
            if is_raise:
                raise exc_val
            else:
                return False
        else:
            return True


from email.mime import image
import os
from common.input_distortion import InputDistortion
import numpy as np
from common.arguments import parse_args
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import os
import sys
import errno
import logging
import datetime
from common.camera import *
from common.loss   import *
from common.data   import *
from common.render import *
from common.model  import *
from common.evaluate import evaluate, run_evaluation
from common.model_factory import get_model, initialize_model
from common.generators import ChunkedGenerator, UnchunkedGenerator, EvaluateGenerator
from time import time
from common.utils import deterministic_random, load_cfg_from_file, set_momentum
from tqdm import tqdm
import os.path as osp
# Parse argument
args = parse_args()
# Load arguments from file
args = load_cfg_from_file(args, args.cfg_file)
print(args, "\n")
# Set GPU device
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]=args.gpu
# Making the experiments reproducible
import random
random.seed(args.seed)
np.random.seed(args.seed)
torch.manual_seed(args.seed)

# Create checkpoint directory
if args.checkpoint == "auto":
    checkpoint_root = "checkpoint"
    args.checkpoint = osp.join(checkpoint_root, "%s-%s-a%s-b%d-dj_%s-dp_%s-df%s-lss_exc_%s-conf_%s%s%s" % (
        args.model, args.keypoints, args.architecture, args.batch_size,
        args.train_distortion_type, args.train_distortion_parts, args.train_distortion_temporal,
        args.loss_ignore_parts,
        'det' if ('hrnet' in args.keypoints) and  args.drop_conf_score == False else args.train_gen_conf_score,
        '_' + args.loss if args.loss != 'mpjpe' else '',
        '_smthconf' if args.smooth_conf_score else ''
        ))
print("Saving checkpoint to ", args.checkpoint, "\n")
try:
    # Create checkpoint directory if it does not exist
    os.makedirs(args.checkpoint)
except OSError as e:
    if e.errno != errno.EEXIST:
        raise RuntimeError('Unable to create checkpoint directory:', args.checkpoint)

# Prepare input distortion instance
inp_distr = InputDistortion(args)
# Prepare dataset
dtf = DataFetcher(args)
# Fetch validation dataset
cameras_valid, poses_valid, poses_valid_2d = dtf.fetch_test()
# Apply distortion on validation data input
poses_valid_2d = [inp_distr.get_test_inputs(i) for i in poses_valid_2d]

model_pos, model_pos_train, checkpoint = initialize_model(args,
    num_joints_in  = poses_valid_2d[0].shape[-2] if len(poses_valid_2d) > 0 else 17,
    num_joints_out = dtf.dataset.skeleton().num_joints(),
    in_features    =  poses_valid_2d[0].shape[-1] if len(poses_valid_2d) > 0 else 3 if args.drop_conf_score == False else 2,
    chk_filename   = os.path.join(args.checkpoint, args.resume if args.resume else args.evaluate)
)

model_traj = None
# Generate test data
if hasattr(model_pos, 'receptive_field'):
    receptive_field = model_pos.receptive_field()
    pad = (receptive_field-1)//2 # Padding on each side
else:
    receptive_field = 1
    for i in args.architecture.split(','):
        receptive_field *= int(i)
        pad = (receptive_field-1)//2
print('INFO: Receptive field: {} frames'.format(receptive_field))

if args.causal:
    print('INFO: Using causal convolutions')
    causal_shift = pad
else:
    causal_shift = 0

if args.test_fixed_size_input:
    test_generator = ChunkedGenerator(args.batch_size // args.stride, cameras_valid, poses_valid, poses_valid_2d, args.stride,
                                  pad=pad, causal_shift=causal_shift, augment=args.test_time_augmentation, shuffle=False,
                                  kps_left=dtf.kps_left, kps_right=dtf.kps_right, joints_left=dtf.joints_left, joints_right=dtf.joints_right)
else:
    test_generator = UnchunkedGenerator(cameras_valid, poses_valid, poses_valid_2d,
                                        pad=pad, causal_shift=causal_shift, augment=False,
                                        kps_left=dtf.kps_left, kps_right=dtf.kps_right, joints_left=dtf.joints_left, joints_right=dtf.joints_right)
print('INFO: Testing on {} frames'.format(test_generator.num_frames()))

################### TRAINING ###################
if not args.evaluate:
    # Prepare log dir
    import wandb
    wandb.init(project="robustvp3d", name=args.wandb_name, config=args)

    logging.basicConfig(filename= #"test.log",
    osp.join(args.checkpoint, "%s.log" % datetime.datetime.now()),
    filemode='a',
    format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
    datefmt='%H:%M:%S',
    level=logging.INFO)
    logging.getLogger().addHandler(logging.StreamHandler())
    # Save args to log file
    logging.info(args)

    # Fetch train data
    cameras_train, poses_train, poses_train_2d = dtf.fetch_train()
    # Apply distortion on input training data
    # from matplotlib import pyplot as plt
    # plt.plot(poses_train_2d[0][:,11,0])
    poses_train_2d = [inp_distr.get_train_inputs(i) for i in poses_train_2d]
    # Prepare optimizers
    lr = args.learning_rate
    optimizer = optim.Adam(model_pos_train.parameters(), lr=lr, amsgrad=True)
    lr_decay = args.lr_decay

    min_loss = 1e4
    losses_3d_train = []
    losses_3d_train_eval = []
    losses_3d_valid = []

    epoch = 0
    initial_momentum = 0.1
    final_momentum = 0.001
    train_generator = ChunkedGenerator(args.batch_size//args.stride, cameras_train, poses_train, poses_train_2d, args.stride,
                                       pad=pad, causal_shift=causal_shift, shuffle=True, augment=args.data_augmentation,
                                       kps_left=dtf.kps_left, kps_right=dtf.kps_right, joints_left=dtf.joints_left, joints_right=dtf.joints_right)
    train_generator_eval = UnchunkedGenerator(cameras_train, poses_train, poses_train_2d,
                                              pad=pad, causal_shift=causal_shift, augment=False)
    print('INFO: Training on {} frames'.format(train_generator_eval.num_frames()))
    if args.resume:
        epoch = checkpoint['epoch']
        if 'optimizer' in checkpoint and checkpoint['optimizer'] is not None:
            optimizer.load_state_dict(checkpoint['optimizer'])
            train_generator.set_random_state(checkpoint['random_state'])
        else:
            print('WARNING: this checkpoint does not contain an optimizer state. The optimizer will be reinitialized.')

        lr = checkpoint['lr']

    print('** Note: reported losses are averaged over all frames and test-time augmentation is not used here.')
    print('** The final evaluation will be carried out after the last training epoch.')

    # Pos model only
    while epoch < args.epochs:
        start_time = time()
        epoch_loss_3d_train = 0
        epoch_loss_traj_train = 0
        epoch_loss_2d_train_unlabeled = 0
        N = 0
        N_semi = 0
        model_pos_train.train()
        # Regular supervised scenario
        for _, batch_3d, batch_2d in tqdm(train_generator.next_epoch(), total=train_generator.num_batches):
            inputs_3d = torch.from_numpy(batch_3d.astype('float32'))
            inputs_2d = torch.from_numpy(batch_2d.astype('float32'))
            if torch.cuda.is_available():
                inputs_3d = inputs_3d.cuda()
                inputs_2d = inputs_2d.cuda()
            inputs_3d[:, :, 0] = 0

            optimizer.zero_grad()
            # inputs_2d = inp_distr.smooth_conf_scr(inputs_2d)
            # Predict 3D poses
            predicted_3d_pos = model_pos_train(inputs_2d)
            # Select joints for computing losses
            predicted_3d_pos = inp_distr.get_loss_joints(predicted_3d_pos)
            inputs_3d        = inp_distr.get_loss_joints(inputs_3d)

            if   args.loss == 'mpjpe':
                loss_3d_pos = mpjpe(predicted_3d_pos, inputs_3d)
            elif args.loss == 'l1':
                loss_3d_pos = L1_loss(predicted_3d_pos, inputs_3d)
            elif args.loss == "conf_mpjpe":
                loss_3d_pos = conf_mpjpe(predicted_3d_pos, inputs_3d, inputs_2d[...,-1])
            epoch_loss_3d_train += inputs_3d.shape[0]*inputs_3d.shape[1] * loss_3d_pos.item()
            N += inputs_3d.shape[0]*inputs_3d.shape[1]

            loss_total = loss_3d_pos
            loss_total.backward()
            optimizer.step()
            wandb.log({"loss_3d_pos": loss_3d_pos.item()*1000})

        losses_3d_train.append(epoch_loss_3d_train / N)

        # End-of-epoch evaluation
        with torch.no_grad():
            model_pos.load_state_dict(model_pos_train.state_dict())
            model_pos.eval()
            epoch_loss_3d_valid = 0
            epoch_loss_traj_valid = 0
            epoch_loss_2d_valid = 0
            N = 0

            if not args.no_eval:
                # Evaluate on test set
                for cam, batch, batch_2d in test_generator.next_epoch():
                    inputs_3d = torch.from_numpy(batch.astype('float32'))
                    inputs_2d = torch.from_numpy(batch_2d.astype('float32'))
                    if args.smooth_conf_score == True or "PoseFormer" in args.model:
                        inputs_2d, inputs_3d = eval_data_prepare(inputs_2d, inputs_3d, receptive_field)

                    if torch.cuda.is_available():
                        inputs_3d = inputs_3d.cuda()
                        inputs_2d = inputs_2d.cuda()
                    inputs_traj = inputs_3d[:, :, :1].clone()
                    inputs_3d[:, :, 0] = 0

                    # Smooth conf. score (if enabled)
                    # inputs_2d = inp_distr.smooth_conf_scr(inputs_2d)
                    # Predict 3D poses
                    predicted_3d_pos = model_pos(inputs_2d)
                    # Select joints for evaluation
                    predicted_3d_pos = inp_distr.get_eval_joints(predicted_3d_pos)
                    inputs_3d        = inp_distr.get_eval_joints(inputs_3d)
                    try:
                        loss_3d_pos = mpjpe(predicted_3d_pos, inputs_3d)
                    except:
                        import ipdb; ipdb.set_trace()
                        loss_3d_pos = mpjpe(predicted_3d_pos[:, :inputs_3d.shape[1]], inputs_3d)
                    epoch_loss_3d_valid += inputs_3d.shape[0]*inputs_3d.shape[1] * loss_3d_pos.item()
                    N += inputs_3d.shape[0]*inputs_3d.shape[1]

                losses_3d_valid.append(epoch_loss_3d_valid / N)

                # Evaluate on training set, this time in evaluation mode
                epoch_loss_3d_train_eval = 0
                epoch_loss_traj_train_eval = 0
                epoch_loss_2d_train_labeled_eval = 0
                N = 1 # SHOULD BE ZERO!!
                # for cam, batch, batch_2d in train_generator_eval.next_epoch():
                #     if batch_2d.shape[1] == 0:
                #         # This can only happen when downsampling the dataset
                #         continue

                #     inputs_3d = torch.from_numpy(batch.astype('float32'))
                #     inputs_2d = torch.from_numpy(batch_2d.astype('float32'))

                #     if args.smooth_conf_score == True or "PoseFormer" in args.model:
                #         inputs_2d, inputs_3d = eval_data_prepare(inputs_2d, inputs_3d, receptive_field)

                #     if torch.cuda.is_available():
                #         inputs_3d = inputs_3d.cuda()
                #         inputs_2d = inputs_2d.cuda()
                #     inputs_traj = inputs_3d[:, :, :1].clone()
                #     inputs_3d[:, :, 0] = 0
                #     # Smooth conf. score (if enabled)
                #     # inputs_2d = inp_distr.smooth_conf_scr(inputs_2d)
                #     # Compute 3D poses
                #     predicted_3d_pos = model_pos(inputs_2d)
                #     # Select joints for computing losses
                #     predicted_3d_pos = inp_distr.get_loss_joints(predicted_3d_pos)
                #     inputs_3d        = inp_distr.get_loss_joints(inputs_3d)

                #     loss_3d_pos = mpjpe(predicted_3d_pos, inputs_3d)
                #     epoch_loss_3d_train_eval += inputs_3d.shape[0]*inputs_3d.shape[1] * loss_3d_pos.item()
                #     N += inputs_3d.shape[0]*inputs_3d.shape[1]

                losses_3d_train_eval.append(epoch_loss_3d_train_eval / N)
                wandb.log({"loss_3d_pos_valid": losses_3d_valid[-1]*1000})
                # Evaluate 2D loss on unlabeled training set (in evaluation mode)
                epoch_loss_2d_train_unlabeled_eval = 0
                N_semi = 0

        elapsed = (time() - start_time)/60

        if args.no_eval:
            logging.info('[%d] time %.2f lr %f 3d_train %f' % (
                    epoch + 1,
                    elapsed,
                    lr,
                    losses_3d_train[-1] * 1000))
        else:
            logging.info('[%d] time %.2f lr %f 3d_train %f 3d_eval %f 3d_valid %f' % (
                    epoch + 1,
                    elapsed,
                    lr,
                    losses_3d_train[-1] * 1000,
                    losses_3d_train_eval[-1] * 1000,
                    losses_3d_valid[-1]  *1000))

        # Decay learning rate exponentially
        lr *= lr_decay
        for param_group in optimizer.param_groups:
            param_group['lr'] *= lr_decay
        epoch += 1

        # Decay BatchNorm momentum
        # momentum = initial_momentum * np.exp(-epoch/args.epochs * np.log(initial_momentum/final_momentum))
        # if args.parallel:
        #     set_momentum(model_pos_train, momentum)
        # else:
        #     model_pos_train.set_bn_momentum(momentum)

        # Save checkpoint if necessary
        def save_ckpt(chk_path):
            print('Saving checkpoint to', chk_path)
            torch.save({
                'epoch': epoch,
                'lr': lr,
                'random_state': train_generator.random_state(),
                'optimizer': optimizer.state_dict(),
                'model_pos': model_pos_train.state_dict(),
                'model_traj': None,
                'random_state_semi':  None,
            }, chk_path)
        if epoch % args.checkpoint_frequency == 0:
            save_ckpt(os.path.join(args.checkpoint, 'epoch_{}.bin'.format(epoch)))
        if len(losses_3d_valid) > 0:
            if  losses_3d_valid[-1] * 1000 < min_loss:
                save_ckpt(os.path.join(args.checkpoint, 'best.bin'.format(epoch)))
                min_loss = losses_3d_valid[-1] * 1000

        # Save training curves after every epoch, as .png images (if requested)
        if args.export_training_curves and epoch > 3:
            if 'matplotlib' not in sys.modules:
                import matplotlib
                matplotlib.use('Agg')
                import matplotlib.pyplot as plt

            plt.figure()
            epoch_x = np.arange(3, len(losses_3d_train)) + 1
            plt.plot(epoch_x, losses_3d_train[3:], '--', color='C0')
            plt.plot(epoch_x, losses_3d_train_eval[3:], color='C0')
            plt.plot(epoch_x, losses_3d_valid[3:], color='C1')
            plt.legend(['3d train', '3d train (eval)', '3d valid (eval)'])
            plt.ylabel('MPJPE (mm)')
            plt.xlabel('Epoch')
            plt.xlim((3, epoch))
            plt.savefig(os.path.join(args.checkpoint, 'loss_3d.png'))
            plt.close('all')

def gen_animation(model_pos, input_keypoints):
    ground_truth = None
    if args.viz_subject in dtf.dataset.subjects() and args.viz_action in dtf.dataset[args.viz_subject]:
        if 'positions_3d' in dtf.dataset[args.viz_subject][args.viz_action]:
            ground_truth = dtf.dataset[args.viz_subject][args.viz_action]['positions_3d'][args.viz_camera].copy()

    if ground_truth is None:
        print('INFO: this action is unlabeled. Ground truth will not be rendered.')
    gen = UnchunkedGenerator(None, None, [input_keypoints],
                             pad=pad, causal_shift=causal_shift, augment=args.test_time_augmentation,
                             kps_left=dtf.kps_left, kps_right=dtf.kps_right, joints_left=dtf.joints_left, joints_right=dtf.joints_right)

    # First set of perdiction
    prediction = evaluate(model_pos, gen, inp_distr, dtf, return_predictions=True)

    if model_traj is not None and ground_truth is None:
        raise NotImplementedError

    if args.viz_export is not None:
        print('Exporting joint positions to', args.viz_export)
        # Predictions are in camera space
        np.save(args.viz_export, prediction)

    if ground_truth is not None:
        # Reapply trajectory
        trajectory = ground_truth[:, :1]
        ground_truth[:, 1:] += trajectory
        prediction += trajectory

    # Invert camera transformation
    cam = dtf.dataset.cameras()[args.viz_subject][args.viz_camera]
    if ground_truth is not None:
        prediction = camera_to_world(prediction, R=cam['orientation'], t=cam['translation'])
        ground_truth = camera_to_world(ground_truth, R=cam['orientation'], t=cam['translation'])
    else:
        # If the ground truth is not available, take the camera extrinsic params from a random subject.
        # They are almost the same, and anyway, we only need this for visualization purposes.
        for subject in dtf.dataset.cameras():
            if 'orientation' in dtf.dataset.cameras()[subject][args.viz_camera]:
                rot = dtf.dataset.cameras()[subject][args.viz_camera]['orientation']
                break
        prediction = camera_to_world(prediction, R=rot, t=0)
        # We don't have the trajectory, but at least we can rebase the height
        prediction[:, :, 2] -= np.min(prediction[:, :, 2])
    return prediction, ground_truth

################### RENDERING ###################
if args.render:
    print('Rendering...')
    input_keypoints       = dtf.keypoints[args.viz_subject][args.viz_action][args.viz_camera].copy()
    noisy_input_keypoints = inp_distr.get_test_inputs(input_keypoints)
    # Predict from clean inputs:
    # prediction, ground_truth = gen_animation(input_keypoints)
    prediction_source_1, ground_truth = gen_animation(model_pos, noisy_input_keypoints)
    # Second set of prediction
    model_pos_2, _, _ = initialize_model(args,
        num_joints_in  = poses_valid_2d[0].shape[-2] if len(poses_valid_2d) > 0 else 17,
        num_joints_out = dtf.dataset.skeleton().num_joints(),
        in_features    =  poses_valid_2d[0].shape[-1] if len(poses_valid_2d) > 0 else 3 if args.drop_conf_score == False else 2,
        chk_filename   = os.path.join(args.checkpoint_2, args.resume if args.resume else args.evaluate)
    )
    prediction_source_2, _ = gen_animation(model_pos_2, noisy_input_keypoints)

    anim_output = {
        'Model 1': prediction_source_1,
        'Model 2': prediction_source_2
        }
    if ground_truth is not None and not args.viz_no_ground_truth:
        anim_output['Ground truth'] = ground_truth
    from common.h36m_dataset import h36m_cameras_intrinsic_params
    cam = h36m_cameras_intrinsic_params[args.viz_camera]

    # input_keypoints       = image_coordinates(input_keypoints[..., :2], w=cam['res_w'], h=cam['res_h'])
    noisy_input_keypoints = image_coordinates(noisy_input_keypoints[..., :2], w=cam['res_w'], h=cam['res_h'])
    ignore_joints = None
    # ignore_joints = np.where(inp_distr.get_mask(inp_distr.body_parts) == 0)[0]
    from common.visualization import dist_render_animation, render_animation

    dist_render_animation(noisy_input_keypoints, dtf.keypoints_metadata, anim_output,
                        dtf.dataset.skeleton(), dtf.dataset.fps(), args.viz_bitrate, cam['azimuth'], args.viz_output,
                        limit=args.viz_limit, downsample=args.viz_downsample, size=args.viz_size,
                        input_video_path=args.viz_video, viewport=(cam['res_w'], cam['res_h']),
                        input_video_skip=args.viz_skip, ignore_joints=ignore_joints)
else:
    print('Evaluating...')

    if not args.by_subject:
        run_evaluation(model_pos, actions=dtf.all_actions, args=args, dtf=dtf, inp_distr=inp_distr, action_filter=dtf.action_filter)
    else:
        for subject in dtf.all_actions_by_subject.keys():
            print('Evaluating on subject', subject)
            run_evaluation(model_pos, actions=dtf.all_actions_by_subject[subject], args=args, dtf=dtf, inp_distr=inp_distr, action_filter=dtf.action_filter)
            print('')


import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

datos= np.loadtxt("datos_CAMINATA.txt")

plt.hist(datos[0], bins=10, normed = True)
plt.savefig("binomial.png")
plt.close()

pasos=[]
i=0
while True:
	if i > (len(datos)-1):
		break
	else:
		pasos.append(np.sum(np.array(datos[i])))
		i+=1

#loc=media scale=desviacion
loc, scale = norm.fit(pasos)

x=np.linspace(3300, 3700, 100)
y=norm.pdf(x, loc, scale)
plt.hist(pasos, bins=100, normed= True)
plt.plot(x,y, color = "r")
plt.savefig("normal.png")
plt.close()
#De acuerdo con el teorema central miu_o*M = miu miu_0= miu / M

N= 10
M=1000
miu0= loc/M

#m miu= N*p
p=miu0/(N)

print("La probabilidad de sacar una cara con esta moneda es: %s " % p)


n, t1, t2, k = [int(i) for i in input().split()]

ans = []

for i in range(n):

    v, u = [int(i) for i in input().split()]

    h = max(v*t1*(100 - k)/100 + u*t2, u*t1*(100 - k)/100 + v*t2)
    ans.append((i, h))

ans.sort(key = lambda x : x[1] , reverse = True)

for i, h in ans:
    print(i+1, "{0:.2f}".format(h))


# -*- coding: utf-8 -*-
"""
Created on Tue Apr 10 15:02:20 2018

@author: hhgood
"""

#encoding=utf-8
import io
# allows for image formats other than gif
try:
  # Python2
  import Tkinter as tk
  from urllib2 import urlopen
except ImportError:
  # Python3
  import tkinter as tk
  from urllib.request import urlopen
import requests
import mysql.connector as MySQLdb
import bs4
from PIL import Image,ImageTk
path='https://www.xiachufang.com'
category='/category'
page='?page='
recipe='recipe/'
database='food'
def get_html(url):
    try:
        r = requests.get(url, timeout=30)
        r.raise_for_status
        r.encoding = 'UTF-8'
        return r.text
    except:
        return "someting wrong"
class Data(object):
    def __init__(self,path,category,recipe):
        self.path=path
        self.category=category
        self.recipe=recipe
#    def __del__(self):
#        self.con.close()
#        self.conn.close()
    def connect(self):
        try:
            self.conn=MySQLdb.connect(user='root',password='',database='food')
            self.con=self.conn.cursor()
            return "success connect"
        except:
            return "fail connect"
    def CreateDataTable(self):
# 创建数据库表
#        self.con.execute('drop table food_picture')
#        self.con.execute('drop table food_recipe')
#        self.con.execute('drop table kind_food')
#        self.con.execute('drop table cook_kind_1')
#        self.con.execute('drop table cook_kind_2')
#        self.con.execute('drop table cook_kind_3')

        self.con.execute('create table food_picture(food_url varchar(50),step_words text,step_picture varchar(150)) ')
        self.con.execute('create table food_recipe(food_url varchar(50),recipe varchar(50),amount varchar(50))')
        self.con.execute('create table kind_food(kind_url varchar(50),name varchar(50),url varchar(50))')
        self.con.execute('create table cook_kind_1(name_1 varchar(50))')
        self.con.execute('create table cook_kind_2(name_1 varchar(50),name_2 varchar(50),name_2_url varchar(50)) ')
        self.con.execute('create table cook_kind_3(name_2 varchar(50),name_3 varchar(50),name_3_url varchar(50)) ')
        self.conn.commit()
    def CreateCookBook(self): ###########################table cook_kind_1,2,3
        html=get_html(self.path+self.category)
        soup = bs4.BeautifulSoup(html, 'lxml')
        second_file=soup.find_all('div',class_='cates-list clearfix has-bottom-border pb20 mb20')
        for second in second_file:
            second_index=second.find('h3',class_='font20 m0').text
            self.con.execute('insert into cook_kind_1(name_1) values(%s)',[second_index])
#            third_file=second.find('div',class_='cates-list-all clearfix hidden')
            third_index=second.find_all('h4',class_='font16')
            third_name =second.find_all('ul',class_=' has-bottom-border')
            third_name.append(second.find('ul',class_='cates-border-none has-bottom-border'))
            lenth=len(third_index)
            for i in range(lenth):
                forth_index=third_name[i].find_all('li')
                self.con.execute('insert into cook_kind_2(name_1,name_2) values(%s,%s)',[second_index,third_index[i].text.strip()])
                for j in forth_index:
                    self.con.execute('insert into cook_kind_3(name_2,name_3,name_3_url) values(%s,%s,%s)',[third_index[i].text.strip(),j.a.text,j.a['href']])
        self.conn.commit()

    def get_content(self,url):#################### table food_recipe,food_picture
        html = get_html(url)
        soup = bs4.BeautifulSoup(html, 'lxml')
        title=soup.find('h1',class_='page-title').text.strip()
#        print(title)
#        title=url
        recipe=soup.find('div',class_='ings')
        name=recipe.find_all('td', class_='name')
        unit=recipe.find_all('td', class_='unit')
        for i in range(len(name)):
            self.con.execute('insert into food_recipe(food_url ,recipe ,amount) values(%s,%s,%s)',[title,name[i].text.strip(),unit[i].text.strip()])
        steps_list = soup.find('div', class_='steps')
        steps = steps_list.find_all('li')
        for i in range(len(steps)):
            try:
                img_name = steps[i].find('p',class_='text').text.strip()
            except:
                img_name=None
#            print('  '+img_name)
            try:
                img_url=steps[i].find('img')['src']
            except:
                img_url=None
            self.con.execute('insert into food_picture(food_url ,step_words,step_picture) values(%s,%s,%s)',[title,img_name.encode(),img_url])
    def crawler(self,name):##########################  table kind_food
#        count=1
        page_number=1
        while True:
            new_url=self.path+str(name)+'?page='+str(page_number)
            print(new_url)
            html = get_html(new_url)
            soup = bs4.BeautifulSoup(html, 'lxml')
            cook=soup.find('div',class_='normal-recipe-list')
            if cook==None:
                break
            cook_list=cook.find_all('li')
#            count=len(cook_list)
            for c in cook_list:
                self.con.execute("insert into kind_food(kind_url,name,url) values('%s','%s','%s')"%(str(name),c.p.text.strip(),c.a['href']))
                self.get_content(self.path+c.a['href'])
            page_number+=1
            self.conn.commit()
    def search_1(self,var):
        self.con.execute("select distinct food_url from food_recipe where recipe=%s",[var])
#        self.con.execute("select name from kind_food where url in (select distinct food_url from food_recipe where recipe=%s)",[var])
    def search_2(self,var):
        self.con.execute("select distinct food_url from food_picture where step_words like %s",['%'+var+'%'])
#        self.con.execute("select name from kind_food where url in (select distinct food_url from food_picture where step_words like %s)",['%'+var+'%'])
    def search_3(self,var):
        self.con.execute("select name from kind_food where name like %s",['%'+var+'%'])

data=Data(path,category,recipe)
window=tk.Tk()
h=800
w=1000
window.geometry('%dx%d'%(h,w))
window.title(path)
#tk.Label(window,text=path).pack()
frame_l=tk.Frame(window,width=400)
frame_l.place(x=0,y=0)
frame_r=tk.Frame(window,width=400)
frame_r.place(x=400,y=0)

tk.Label(frame_l,text="功能").pack()

var_1=tk.Variable()
var_1.set("未连接")
var_2=tk.Variable()
var_2.set("未构建")
var_3=tk.StringVar()
var_4=tk.StringVar()
var_5=tk.StringVar()
var_6=tk.StringVar()
var_7=tk.StringVar()
var_7.set('0')
var_8=tk.StringVar()
var_9=tk.StringVar()
var_10=tk.StringVar()
var_11=tk.StringVar()
var_11.set("目录")
var_12=tk.StringVar()
var_12.set("步骤")
var_13=tk.StringVar()
var_13.set("用料")
var_14=tk.StringVar()
var_14.set("搜索结果")

def link():
    var_1.set(data.connect())
tk.Button(frame_l,text='连接数据库',command=link).pack()
def create():
    try:
        data.CreateDataTable()
        data.CreateCookBook()
        data.conn.commit()
        var_2.set("构建成功")
    except:
#        raise
        var_2.set("已构建")
#    data.con.execute('select name_3 from cook_kind_3' )
    data.con.execute('select name_1 from cook_kind_1' )
    var_4.set(data.con.fetchall())
tk.Button(frame_l,text='构建数据库',command=create).pack()

def fill_1():
    value=lb.get(lb.curselection())
    data.con.execute("select name_3_url from cook_kind_3 where name_3='%s'"%(value) )
    result=data.con.fetchone()[0]
    try:
        data.crawler(result)
        var_3.set(result+'success')
    except:
        var_3.set(result+'fail')
        raise
tk.Button(frame_l,text='填充数据库_1',command=fill_1).pack()
tk.Label(frame_l,textvariable=var_3,bg='green',font=('Arial',12)).pack()

def back_detail():
    value=lb.get(lb.curselection())
    data.con.execute("select name_3 from cook_kind_3 where name_2=(select name_2 from cook_kind_3 where name_3_url=(select kind_url from kind_food where name=%s))",value)
    result=data.con.fetchall()
    if result==[]:
        data.con.execute("select name_2 from cook_kind_2 where name_1=(select name_1 from cook_kind_2 where name_2=(select name_2 from cook_kind_3 where name_3=%s))",value)
        result=data.con.fetchall()
        if result==[]:
            data.con.execute("select name_1 from cook_kind_1")
            result=data.con.fetchall()
    var_4.set(result)
    var_11.set('目录('+str(len(result))+')')
tk.Button(frame_l,text='回退细节_1',command=back_detail).pack()

def fill_detail():
    value=lb.get(lb.curselection())
    data.con.execute("select name_2 from cook_kind_2 where name_1='%s'"%(value))
    result=data.con.fetchall()
    if result==[]:
        data.con.execute("select name_3 from cook_kind_3 where name_2='%s'"%(value))
        result=data.con.fetchall()
        if result==[]:
            data.con.execute("select name from kind_food where kind_url=(select name_3_url from cook_kind_3 where name_3='%s')"%(value))
            result=data.con.fetchall()
            if result==[]:
                var_9.set(value)
                var_10.set(value)
                fill_2()
                fill_4()
                return
    var_4.set(result)
    var_11.set('目录('+str(len(result))+')')
tk.Button(frame_l,text='显示细节_1',command=fill_detail).pack()
tk.Label(frame_l,text='搜索输入').pack()

e=tk.Entry(frame_l)
e.pack()
def Search_1():
    var=e.get()
    data.search_1(var)
    result=data.con.fetchall()
#    t.insert('end',result)
    var_5.set(result)
    var_14.set('搜索结果('+str(len(result))+')')
tk.Button(frame_l,text="用料搜索",command=Search_1).pack()
#t.pack()

def Search_2():
    var=e.get()
    data.search_2(var)
    result=data.con.fetchall()
    var_5.set(result)
    var_14.set('搜索结果('+str(len(result))+')')
#    t.insert('end',result)
tk.Button(frame_l,text="工艺搜索",command=Search_2).pack()

def Search_3():
    var=e.get()
    data.search_3(var)
    result=data.con.fetchall()
    var_5.set(result)
    var_14.set('搜索结果('+str(len(result))+')')
#    t.insert('end',result)
tk.Button(frame_l,text="菜名搜索",command=Search_3).pack()
tk.Label(frame_l,textvariable=var_14).pack()
lb_2=tk.Listbox(frame_l,listvariable=var_5)
lb_2.pack()

def fill_2():
    try:
        value=lb_2.get(lb_2.curselection())
    except:
        value=lb.get(lb.curselection())
    data.con.execute("select step_words from food_picture where food_url='%s'"%(value))
    result=data.con.fetchall()
    var_6.set(result)
    var_9.set(value)
    var_12.set('步骤('+str(len(result))+')')
tk.Button(frame_l,text='显示做菜步骤_1_2',command=fill_2).pack()

def fill_4():
    try:
        value=lb_2.get(lb_2.curselection())
    except:
        value=lb.get(lb.curselection())
    data.con.execute("select recipe from food_recipe where food_url=%s",value)
    result=data.con.fetchall()
    var_8.set(result)
    var_10.set(value)
    var_13.set('用料('+str(len(result))+')')
tk.Button(frame_l,text='显示用料_1_2',command=fill_4).pack()

def fill_3():
    value=lb_3.get(lb_3.curselection())
    data.con.execute("select step_picture from food_picture where step_words=%s",value)
    img_url=data.con.fetchone()[0]
    print(img_url)
    if img_url!=None:
        image_bytes = urlopen(img_url).read()
        data_stream = io.BytesIO(image_bytes)
        pil_image = Image.open(data_stream)
        tk_image = ImageTk.PhotoImage(pil_image)
        label.config(image=tk_image)
        label.image=tk_image
    else :
        label.config(text='空')
        label.config(image=None)
tk.Button(frame_l,text='显示图片_3',command=fill_3).pack()
label = tk.Label(frame_l)
label.pack()
tk.Label(frame_l,text='picture').pack()
####################################################################################################################
tk.Label(frame_r,text="结果").pack()
tk.Label(frame_r,textvariable=var_1).pack()
tk.Label(frame_r,textvariable=var_2).pack()
frame_list=tk.Frame(frame_r)
frame_list.pack()
tk.Label(frame_list,textvariable=var_11).pack()
lb=tk.Listbox(frame_list,listvariable=var_4)
lb.pack()

frame_list_2=tk.Frame(frame_r)
frame_list_2.pack()

def count():
    try:
        value=lb_2.get(lb_2.curselection())
    except:
        value=lb.get(lb.curselection())
    data.con.execute("select step_picture from food_picture where food_url='%s'"%(value))
    result=data.con.fetchall()
    number=0
    for i in result:
        if i!=None:
            number+=1
    print(number)
    var_7.set(str(number))
tk.Label(frame_list_2,textvariable=var_7).pack()
tk.Button(frame_list_2,text='图片数量_1_2',command=count).pack()
tk.Label(frame_list_2,textvariable=var_9,bg='green',font=('Arial',12)).pack()
tk.Label(frame_list_2,textvariable=var_12).pack()
lb_3=tk.Listbox(frame_list_2,listvariable=var_6)
lb_3.pack()

#frame=tk.Frame(window,width=1000,height=1000)
#frame.pack()
#t = tk.Text(frame_r,height=2)
#scrollbar.config(command=t.yview)
#scrollbar=tk.Scrollbar(frame,orient='vertical')
#scrollbar.pack(side='right', fill='y')
#window.title('test')

#    with open(value+'.png','wb+') as f:
#        f.write(requests.get(img_url).content)
frame_list_2=tk.Frame(frame_r)
frame_list_2.pack()

#frame_picture=tk.Toplevel()

tk.Label(frame_list_2,textvariable=var_10,bg='green',font=('Arial',12)).pack()
tk.Label(frame_list_2,textvariable=var_13).pack()
lb_4=tk.Listbox(frame_list_2,listvariable=var_8)
lb_4.pack()

#result=0
#def show_me():
#    var=e.get()
#    data.con.execute('select step_words from food_picture where food_url=%s',[var])
#    result=data.con.fetchall()
#    var_6.set(result)
#tk.Button(frame_l,text='search',command=show_me).pack()

window.mainloop()


#!/usr/bin/env python3

#import otmt
import glob
from warcio.warcwriter import WARCWriter
from warcio.statusandheaders import StatusAndHeaders
from hashlib import md5
from datetime import datetime
import traceback
import requests
import csv

def create_warc(urim, raw_urim, output_directory, username):

    m = md5()
    m.update(urim.encode('utf8'))
    urlhash = m.hexdigest()

    resp = requests.get(urim, stream=True)
    resp.raise_for_status()

    headers_list = resp.raw.headers.items()

    raw_response = requests.get(raw_urim, stream=True)

    warc_target_uri = None

    # we have to implement this construct in case the archive combines original with other relations
    for link in resp.links:

        if 'original' in link:
            warc_target_uri = resp.links[link]['url']

    if warc_target_uri is None:
        module_logger.warning("could not find this memento's original resource, skipping {}".format(urim))
        return

    try:
        mdt = resp.headers['Memento-Datetime']

    except KeyError:
        print("could not find this memento's memento-datetime, skipping {}".format(urim))
        return

    http_headers = StatusAndHeaders('200 OK',
        headers_list, protocol='HTTP/1.0')

    # module_logger.debug("mdt formatted by strptime and converted by strftime: {}".format(
    #     datetime.strptime(
    #         mdt, "%a, %d %b %Y %H:%M:%S GMT"
    #     ).strftime('%Y-%m-%dT%H:%M:%SZ')
    # ))

    warc_headers_dict = {}
    # warc_target_uri = "https://twitter.com/realDonaldTrump/status/1338871862315667456"
    # warc_headers_dict['WARC-Refers-To'] = "<urn:uuid:1c88a09f-8bd4-4cb9-82a0-69cc92078204>"
    # warc_headers_dict['WARC-Profile'] = "http://netpreserve.org/warc/1.1/revisit/identical-payload-digest"
    # warc_headers_dict['WARC-Refers-To-Target-URI'] = "http://instagram.com/p/D/"
    # warc_headers_dict['WARC-Refers-To-Date'] = "2019-10-25T02:36:18Z"
    warc_headers_dict['WARC-Date'] = datetime.strptime(
        mdt, "%a, %d %b %Y %H:%M:%S GMT"
    ).strftime('%Y-%m-%dT%H:%M:%SZ')

    with open("{}/{}-{}-{}.warc.gz".format(output_directory, urlhash, datetime.now().strftime('%Y%m%d%H%M%S'),username), 'wb') as output:
        writer = WARCWriter(output, gzip=True)

        record = writer.create_warc_record(warc_target_uri, 'response',
                                        payload=resp.raw,
                                        http_headers=http_headers,
                                        warc_headers_dict=warc_headers_dict)

        writer.write_record(record)

if __name__ == "__main__":
    start = datetime.now()
    with open("urims.csv") as f:
        urims = f.readlines()

    urim_list = []

    for item in urims:
        urim = item.strip("\n")
        parts = urim.split("/web/")
        urir = parts[1].split("/",1)[1]
        raw_urim = parts[0] + '/web/' + parts[1].split("/",1)[0] + 'mp_/' + urir
        with open("post_user_mapping.csv") as csvfile:
            reader = csv.reader(csvfile)
            for row in reader:
                if row[0] == urir:
                    username = row[1]
                else:
                    pass
        #print(username)
        tuple = (urim,raw_urim,username)
        urim_list.append(tuple)
    #print(urim_list)
    #to get username - temporory fix
    #username = "sda"
    output_directory = "/home/marsh/Documents/Research/Thesis/MSThesis/IG_Archive/WARCs/response"
    count = 0
    for each in urim_list:
        urim = each[0]
        raw_urim = each[1]
        username = each[2]
        try:
            create_warc(urim, raw_urim, output_directory,username)
        except Exception as e:
            #print(e)
            print(urim)
        count = count + 1
        print(count)
        #break
    end = datetime.now()
    print("Start: " + str(start))
    print("End: " + str(end))
    print("Time taken: " + str(end - start))


"""
Script to use DB backups to migrate data for covidcast issues and lag addition

Author: Eu Jing Chua
Created: 2020-06-26
"""

# Standard library
import argparse
from collections import defaultdict
import datetime
import glob
import gzip
from itertools import islice, chain, starmap
import logging
from multiprocessing import Pool
import os
from typing import Optional, List, Iterable, Iterator, Dict, Tuple

# Third party
import pandas as pd
from multiprocessing_logging import install_mp_handler, uninstall_mp_handler

COVIDCAST_INSERT_START = "INSERT INTO `covidcast` VALUES "

# Column names
INDEX_COLS = ["source", "signal", "time_type", "geo_type", "time_value", "geo_value"]
VALUE_COLS = ["value_updated_timestamp", "value", "stderr", "sample_size", "direction_updated_timestamp", "direction"]
ALL_COLS = INDEX_COLS + VALUE_COLS
ALL_COLS_WITH_PK = ["id"] + ALL_COLS

# Dtypes that try save memory by using categoricals
DTYPES = {
    # skip "id", the primary key as it may have changed
    "source": "category",
    "signal": "category",
    "time_type": "category",
    "geo_type": "category",
    # time_value as str, because we need this parsed as a datetime anyway
    "time_value": "str",
    "geo_value": "category",
    "value_updated_timestamp": "int",
    "value": "str",
    "stderr": "str",
    "sample_size": "str",
    "direction_updated_timestamp": "int",
    "direction": "category"
}

def parse_args():
    '''
    Commandline arguments
    '''
    parser = argparse.ArgumentParser(
        description="Process DB backups to migrate data for covidcast issues and lag addition")
    parser.add_argument(
        "--input-files", nargs="+", dest="sql_files",
        default=glob.glob("./just_covidcast_*_database.sql.gz"),
        help="Input backup .sql files to process. May be compressed (.gz)")
    parser.add_argument(
        "--skip", nargs="+", dest="skip_sources", default=[],
        help="List of sources to skip")
    parser.add_argument(
        "--tmp-dir", dest="tmp_dir", default="./tmp", type=str,
        help="Temporary directory to use for intermediate files")
    parser.add_argument(
        "--out-dir", dest="out_dir", default="./out", type=str,
        help="Output directory to use for resulting .sql files")
    parser.add_argument(
        "--max-insert-chunk", dest="chunk_size", default=1000, type=int,
        help="Maximum number of rows to have per SQL INSERT statement")
    parser.add_argument(
        "--par", dest="parallel", action="store_true",
        help="Enable multiprocessing")
    parser.add_argument(
        "--ncpu-csvs", dest="ncpu_csvs", default=1, type=int,
        help="Max number of processes to use for CSV processing (low memory usage)")
    parser.add_argument(
        "--ncpu-sources", dest="ncpu_sources", default=1, type=int,
        help="Max number of processes to use for processing sources (high memory usage)")
    parser.add_argument(
        "--incremental", dest="use_cache", action="store_true",
        help="Reuse results in --tmp-dir, and skip over existing results in --out-dir")
    parser.add_argument(
        "--debug", dest="debug", action="store_true",
        help="More verbose debug output")

    args = parser.parse_args()
    return args

def show_args(args):
    '''
    Display arguments being used
    '''
    logging.info("Input files (in order):\n\t%s", "\n\t".join(sorted(args.sql_files)))
    logging.info("Skipping sources: [%s]", ", ".join(args.skip_sources))
    logging.info("Temporary dir: %s", args.tmp_dir)
    logging.info("Output dir: %s", args.out_dir)
    logging.info("Max insert chunk: %d", args.chunk_size)
    logging.info("Parallel: %s", args.parallel)
    if args.parallel:
        logging.info("Num. CPU (CSVs): %d", args.ncpu_csvs)
        logging.info("Num. CPU (sources): %d", args.ncpu_sources)
    logging.info("Incremental: %s", args.use_cache)
    logging.info("Debug output: %s", args.debug)
    print()

def main(args):
    '''
    Overall flow:
        1) Extract relevant tuples from .sql into CSVs so we can use CSV diffing tools
        2) Split each CSV by 'source'
        3) For each source, do a CSV diff for each sliding pair of dates
        4) As diffs are found, write results to a .sql file for current source
    '''

    # 0) Configuration stuff
    logging.basicConfig(
        level=logging.DEBUG if args.debug else logging.INFO,
        format="%(levelname)s:\t%(message)s")

    show_args(args)

    os.makedirs(args.tmp_dir, exist_ok=True)
    os.makedirs(args.out_dir, exist_ok=True)

    # 1) Extract relevant tuples from .sql into CSVs so we can use CSV diffing tools
    logging.info("Extracting to csvs...")
    csv_files = []
    extract_args = []

    # Ensure files are in sorted order of date in filename
    for sql_file in sorted(args.sql_files):
        csv_file = os.path.join(
            args.tmp_dir,
            f"just_covidcast_{date_int_from_filename(sql_file)}.csv")

        if args.use_cache and os.path.exists(csv_file):
            logging.debug("CSV %s already exists, skipping processing of %s", csv_file, sql_file)
        else:
            extract_args.append((sql_file, csv_file))

        # Regardless of cache, keep track of csv files anyway
        csv_files.append(csv_file)

    starmap_mp_logging(
        extract_to_csv, extract_args,
        par=args.parallel, ncpu=args.ncpu_csvs)

    # 2) Split each backup's csv by source
    logging.info("Splitting csvs...")
    split_col = 1
    split_csv_args = []
    files_by_src = defaultdict(list)

    for csv_file in csv_files:
        base_name, f_ext = os.path.splitext(csv_file)
        split_patt = f"{base_name}_*{f_ext}"
        split_csv_files = glob.glob(split_patt)

        if args.use_cache and len(split_csv_files) > 0:
            logging.debug("CSV %s already split, skipping splitting", csv_file)

            # If split csvs already exist, update files_by_src directly
            for sub_csv_file in split_csv_files:
                src = sub_csv_file.split("_")[-1][:-4]
                files_by_src[src].append(sub_csv_file)
        else:
            split_csv_args.append((csv_file, split_col))

    by_srcs = starmap_mp_logging(
        split_csv_by_col, split_csv_args,
        par=args.parallel, ncpu=args.ncpu_csvs)

    # Combine all return dictionaries into a dictionary of lists instead
    # Note that each list may not be sorted
    for by_src in by_srcs:
        for src, sub_csv_file in by_src.items():
            files_by_src[src].append(sub_csv_file)

    # 3) Find issues from sliding pairs of [None, csv_1, csv_2, ... csv_N] for each source
    proc_args = []
    for source, src_files in files_by_src.items():
        if source in args.skip_sources:
            logging.info("Skipping group: %s", source)
            continue

        proc_args.append((args, source, src_files))

    output_sql_files = starmap_mp_logging(
        process_source, proc_args,
        par=args.parallel, ncpu=args.ncpu_sources)

    return output_sql_files

def starmap_mp_logging(func, args: Iterable, par: bool = False, ncpu: Optional[int] = None):
    '''
    Does a starmap of f over args, either in parallel or serially, with logging support

    Args:
        func: Callable to execute with each of args
        args: List-like of args to execute f with
        par: Whether to run in parallel or not
        ncpu: When par=True, how many processes to use

    Returns:
        Result equivalent to starmap(f, args)
    '''
    if par:
        install_mp_handler()
        try:
            with Pool(ncpu) as pool:
                return pool.starmap(func, args)
        finally:
            uninstall_mp_handler()
    else:
        return starmap(func, args)

def process_source(args, source: str, src_files: List[str]) -> List[List[str]]:
    logging.info("[%s] Finding issues and generating SQL files...", source)
    files = [None] + sorted(src_files)
    output_files = []

    for before_file, after_file in zip(files, files[1:]):
        date_int_after = date_int_from_filename(after_file)
        if before_file is None:
            logging.debug("[%s] First: %s", source, date_int_after)
            outfile = os.path.join(args.out_dir, f"{source}_00000000_{date_int_after}.sql")
        else:
            date_int_before = date_int_from_filename(before_file)
            logging.debug("[%s] Diffing: from %s to %s", source, date_int_before, date_int_after)
            outfile = os.path.join(args.out_dir, f"{source}_{date_int_before}_{date_int_after}.sql")

        # Diff and find new issues
        if args.use_cache and os.path.exists(outfile):
            logging.debug(
                "[%s] SQL file %s already generated, skipping diff",
                source, outfile)
            output_files.append(outfile)
            continue

        issues = generate_issues(before_file, after_file)

        # 4) Write out found issues into the SQL file
        logging.debug("[%s] Writing to %s", source, outfile)
        try:
            with open(outfile, "w") as f_sql:
                for issues_chunk in chunked(issues, args.chunk_size):
                    insert_stmt = COVIDCAST_INSERT_START + \
                        ",\n".join(issues_chunk) + \
                        ";\n"
                    f_sql.write(insert_stmt)
            output_files.append(outfile)

        except Exception as ex:
            logging.error(
                "[%s] Stopped unexpectedly while writing %s, deleting it",
                source, outfile, exc_info=True)
            os.remove(outfile)
            raise ex

    return output_files

def extract_to_csv(filename: str, output: str):
    '''
    Takes a backup .sql file and produces a CSV representing just the covidcast rows.
    Also accepts gzipped .sql.gz files as input.

    Args:
        filename: Input .sql or .sql.gz file
        output: Output .csv file
    '''

    logging.debug("Processing %s into %s", filename, output)

    is_covidcast = lambda line: line.startswith(COVIDCAST_INSERT_START)

    # Open gzipped .sql file or regular .sql file
    if filename.endswith(".gz"):
        open_file = lambda fname: gzip.open(fname, "rt")
    else:
        open_file = lambda fname: open(fname, "r")

    # Load bulk insert lines
    with open_file(filename) as f_in:

        # Try to keep everything as iterators to reduce memory usage
        inserts = filter(is_covidcast, f_in)

        # Extract just tuples as individual lines
        old_sep, new_sep = "),(", "\n"

        # Skip the initial insert statement and (, and trailing ');\n'
        start, end = len(COVIDCAST_INSERT_START) + 1, -3

        with open(output, "w") as f_out:
            for insert_cmd in inserts:
                split_up_insert = insert_cmd[start:end].replace(old_sep, new_sep)
                f_out.write(split_up_insert + "\n")

def split_csv_by_col(
        filename: str, col_idx: int, add_header: bool = True) -> Dict[str, str]:
    '''
    Splits up a CSV file by unique values of a specified column into subset CSVs.
    Produces subset CSVs in same directory as input, with '_{value}' appended to filename.
    Assumes the input CSV has no header row, as produced by extract_to_csv.

    Args:
        filename: Input CSV file
        col_idx: Column index to split-by-values on
        add_header: Add column header row to output CSVs

    Returns:
        Mapping from column value -> subset CSV filename
    '''

    logging.debug("Splitting %s by %s", filename, ALL_COLS_WITH_PK[col_idx])

    open_file_writers = {}
    created_files = {}
    base_name, f_ext = os.path.splitext(filename)

    with open(filename, "r") as f_csv:
        # Assume no header
        prev_value = None
        for line in f_csv:
            # Not using in-built csv module as it was alot slower

            # Dont need the rest of the split beyond the column we are interested in
            value = line.split(",", col_idx + 1)[col_idx]

            # Get appropriate file to write to, and create it if it does not exist yet
            # Since most sources are in continuous rows, try do less dict lookups
            # Only change file handle when we see a different value
            if value != prev_value:
                if value not in open_file_writers:

                    # Strip value of surrounding quotes for nicer filenames
                    clean_value = value.strip("'")
                    created_file = f"{base_name}_{clean_value}{f_ext}"
                    created_files[clean_value] = created_file

                    # Create and store file handle
                    sub_f = open(created_file, "w")
                    open_file_writers[value] = sub_f

                    # Add headers as the first row if indicated
                    if add_header:
                        sub_f.write(",".join(ALL_COLS_WITH_PK) + "\n")
                else:
                    sub_f = open_file_writers[value]

            # Write to appropriate file
            sub_f.write(line)

            prev_value = value

    # Close all sub file handles
    for _, sub_f in open_file_writers.items():
        sub_f.close()

    return created_files

def datetime_to_int(date: datetime.datetime) -> int:
    '''
    Convert input datetime.date to date integer format YYYYmmdd
    '''
    return int(date.strftime("%Y%m%d"))

def int_to_datetime(date_int: int) -> datetime.datetime:
    '''
    Convert input date integer format YYYYmmdd to datetime.datetime
    '''
    return datetime.datetime.strptime(str(date_int),"%Y%m%d")

def date_int_from_filename(filename: str) -> int:
    '''
    Extract date integer from a filename.
    Assumes file is of format '{dir}/just_covidcast_{date}_...{ext}'.
    '''
    file_base = os.path.basename(filename)
    return int(file_base.split("_")[2])

def pd_csvdiff(
        before_file: str, after_file: str,
        index_cols: List[str],
        dtypes: Dict[str, str],
        find_removals: bool = False
        ) -> Tuple[pd.DataFrame, pd.DataFrame]:
    '''
    Finds the diff (additions and changes only by default) between two CSV files.
    Can find removals, but the additional index operations required add significant time.
    Uses pandas with specified dtypes to save some memory.

    Args:
        before_file: The "before" CSV file to diff from
        after_file: The "after" CSV file to diff to
        index_cols: Column names to use as the index that identifies an entry
        dtypes: Dtype definitions for column names to try save memory
        find_removals: Whether to find entries that were removed too

    Returns:
        A dataframe containing a subset of the after_file CSV that represents additions and changes
    '''
    df_before = pd.read_csv(
        before_file, usecols=dtypes.keys(), parse_dates=["time_value"],
        dtype=dtypes, na_filter=False)
    df_after = pd.read_csv(
        after_file, usecols=dtypes.keys(), parse_dates=["time_value"],
        dtype=dtypes, na_filter=False)

    # Efficiently union all categories together for comparison
    for col, dtype in dtypes.items():
        if dtype == "category":
            before_cats = df_before[col].cat.categories
            after_cats = df_after[col].cat.categories
            df_before[col].cat.add_categories(after_cats.difference(before_cats), inplace=True)
            df_after[col].cat.add_categories(before_cats.difference(after_cats), inplace=True)

            assert df_before[col].dtype == df_after[col].dtype

    df_before.set_index(index_cols, inplace=True)
    df_after.set_index(index_cols, inplace=True)

    # Ensure lex sorted indices for efficient indexing
    df_before.sort_index(inplace=True)
    df_after.sort_index(inplace=True)

    # Find additions and changes together
    # Re-index df_before to be like df_after, index-wise, then do a diff
    # For common indices, different field values be false in same_mask
    # Since df_before is filled with NaN for new indices, new indices turn false in same_mask
    same_mask = (df_before.reindex(df_after.index) == df_after)

    # Ignore direction_updated_timestamp in the diff
    is_diff = ~(same_mask.loc[:, same_mask.columns != "direction_updated_timestamp"].all(axis=1))

    # Removed indices can be found via index difference, but is expensive
    if find_removals:
        removed_idx = df_before.index.difference(df_after.index)

        return (
            df_after.loc[is_diff, :],
            df_before.loc[removed_idx, :])

    return (
        df_after.loc[is_diff, :],
        None)

def generate_issues(
        before_file: Optional[str], after_file: str) -> Iterator[str]:
    '''
    A generator that diffs the input files, then yields formatted strings representing a row-tuple
    to be inserted in SQL. If before_file is None, we are simplying filling it with entries from
    after_file. The issue date for these "first" entries come from the after_file filename.
    Otherwise, we are updating accum with the diff between before_file to after_file.

    Args:
        before_file: The "before" CSV file in diffing. None if after_file is the 1st
        after_file: The "after" CSV file in diffing.

    Returns:
        An iterator that yields the string row-tuples to be inserted as an issue.
    '''

    # Get issue date from after_file
    issue_date_int = date_int_from_filename(after_file)
    issue_date = int_to_datetime(issue_date_int)

    row_fmt = "(" \
        "{id},{source},{signal},{time_type},{geo_type},{time_value},{geo_value}," \
        "{row.value_updated_timestamp},{row.value},{row.stderr},{row.sample_size},{row.direction_updated_timestamp},{row.direction}," \
        "{issue},{row.lag})"

    try:
        if before_file is None:
            # At first file, just yield all contents as new issues
            df_diff = pd.read_csv(
                after_file, usecols=DTYPES.keys(), parse_dates=["time_value"],
                dtype=DTYPES, index_col=INDEX_COLS, na_filter=False)
        else:
            # Perform the CSV diff using INDEX_COLS to identify rows
            df_diff, _ = pd_csvdiff(before_file, after_file, INDEX_COLS, DTYPES)
    except Exception as ex:
        logging.error(
            "Diff Failed!!! Between files '%s', '%s'",
            before_file, after_file, exc_info=True)
        raise ex

    # TODO: Does not really handle weekly values properly. Weekly time_value are in YYYYww format
    df_diff["lag"] = (issue_date - df_diff.index.get_level_values("time_value")).days
    is_weekly = df_diff.index.get_level_values("time_type") == "week"
    df_diff.loc[is_weekly, "lag"] = df_diff.loc[is_weekly, "lag"] // 7

    for row in df_diff.itertuples():
        index = dict(zip(INDEX_COLS, row.Index))
        index["time_value"] = datetime_to_int(index["time_value"])

        yield row_fmt.format(id=0, **index, row=row, issue=issue_date_int)

def chunked(iterable: Iterable, size) -> Iterator[Iterator]:
    '''
    Chunks an iterable into desired size without walking whole iterable first.
    https://stackoverflow.com/questions/24527006/split-a-generator-into-chunks-without-pre-walking-it
    '''
    iterator = iter(iterable)
    for first in iterator:
        yield chain([first], islice(iterator, size - 1))

if __name__ == "__main__":
    main(parse_args())


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 22 14:32:51 2022

@author: dineshverma
"""

from flask import Flask
from flask_swagger_ui import get_swaggerui_blueprint
import os

# from views import self_describing_bp
# from models import SelfDescribingEntryDbase
from catalog.errors import _initialize_errorhandlers
from catalog.sda_view import _initialize_sda_views
from catalog.user_view import _initialize_user_views

def create_app(test_config=None):
    app = Flask(__name__, instance_relative_config=True)
    if test_config is None:
        # load the test configuration from instance path
        app.config.from_pyfile('config.py', silent=True)
    else:
        app.config.from_mapping(test_config)

    # Make Sure that instance_path exists
    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    # from catalog.views import sda_blueprint
    # app.register_blueprint(sda_blueprint)

    # Swagger config
    SWAGGER_URL = '/apispec'
    API_URL = '/static/apispec.json'
    SWAGGER_BLUEPRINT = get_swaggerui_blueprint(
        SWAGGER_URL,
        API_URL,
        config={
            'app_name': "SDA API",
            'defaultModelsExpandDepth': -1,
            'defaultModelRendering': "model",
            'defaultModelExpandDepth': 2
        }
    )
    app.register_blueprint(SWAGGER_BLUEPRINT, url_prefix=SWAGGER_URL)

    _initialize_errorhandlers(app)
    _initialize_sda_views(app)
    _initialize_user_views(app)

    # Test configuration
    @app.route('/hello', methods=['GET', 'POST'])
    def hello():
        answer = "<p> " + app.instance_path + " <p>"
        for this_key in app.config.keys():
            answer = answer + "<p> " + this_key + ":" + str(app.config[this_key]) + "</p>"

        return answer

    @app.route('/shello', methods=['GET', 'POST'])
    def shello():
        answer = "<p> " + "Simple Hello " + "<p>"
        return answer

    return app


"""
entity types and ids

RPGAdventuringGear    adventuring-gear   2103445194
RPGArmor              armor              701257905
RPGMagicItem          magic-item         112130694
RPGWeapon             weapon             1782728300
"""

import abc

from .mixins import DescribableMixin
from .shared import Sourced

class Item(DescribableMixin, Sourced, abc.ABC):
    def __init__(
        self, name: str, desc: str, attunement: bool | str, meta: str | None = None, image: str | None = None, **kwargs
    ):
        super().__init__(**kwargs)
        self.name = name
        self.desc = desc
        self.attunement = attunement
        self.meta = meta
        self.image = image

    @classmethod
    def from_data(cls, d):
        return cls(
            d["name"],
            d["desc"],
            d["attunement"],
            d.get("meta"),
            d.get("image"),
            homebrew=False,
            source=d["source"],
            entity_id=d["id"],
            page=d["page"],
            url=d["url"],
            is_free=d["isFree"],
            is_legacy=d.get("isLegacy", False),
        )

    @classmethod
    def from_homebrew(cls, d, source):
        return cls(d["name"], d["desc"], False, d.get("meta"), d.get("image"), source=source, homebrew=True)

    @property
    def description(self):
        return self.desc

class AdventuringGear(Item):
    entity_type = "adventuring-gear"
    type_id = 2103445194

class Armor(Item):
    entity_type = "armor"
    type_id = 701257905

class MagicItem(Item):
    entity_type = "magic-item"
    type_id = 112130694

class Weapon(Item):
    entity_type = "weapon"
    type_id = 1782728300

class HomebrewItem(MagicItem):
    """
    Avrae homebrew items don't really care about their item type - we just call them all magic items but give them their
    own class to separate them
    """

    pass


from pyteal import *

class Wear:
    class AppMethods:
        buy = Bytes("buy")
        change_discount = Bytes("change_discount")
        update_stock = Bytes("update-stock")

    class Variables:
        creator = Bytes("_creator")
        name = Bytes("_name")
        description = Bytes("_description")
        amount = Bytes("_amount")
        image = Bytes("_image")
        stock = Bytes("_stock")
        discount = Bytes("_discount")

    def application_creation(self):
        return Seq([
            # check length of args
            Assert(Txn.application_args.length() == Int(6)),
            # check contract note
            Assert(Txn.note() == Bytes("wear:uv3")),
            # check that price is greater than 0
            Assert(Btoi(Txn.application_args[3]) > Int(0)),

            # intialize variables
            App.globalPut(self.Variables.creator, Txn.sender()),
            App.globalPut(self.Variables.name, Txn.application_args[0]),
            App.globalPut(self.Variables.description, Txn.application_args[1]),
            App.globalPut(self.Variables.image, Txn.application_args[2]),
            App.globalPut(self.Variables.amount,
                          Btoi(Txn.application_args[3])),
            App.globalPut(self.Variables.stock,
                          Btoi(Txn.application_args[4])),
            App.globalPut(self.Variables.discount,
                          Btoi(Txn.application_args[5])),

            Approve()
        ])

    def buy(self):
        discounted_amount = App.globalGet(
            self.Variables.amount) - App.globalGet(self.Variables.discount)
        return Seq([
            Assert(
                #use and operation
                And(
                    # check if the size of transaction is two
                    Global.group_size() == Int(2),
                    # check if the arguments passed is 1
                    Txn.application_args.length() == Int(1),
                    # check that sender is not the creator
                    # Txn.sender() != App.globalGet(self.Variables.creator),
                    # check that stock is not 0
                    App.globalGet(self.Variables.stock) > Int(0),

                    Gtxn[1].type_enum() == TxnType.Payment,
                    Gtxn[1].receiver() == App.globalGet(
                        self.Variables.creator),
                    Gtxn[1].amount() == discounted_amount,
                    Gtxn[1].sender() == Gtxn[0].sender(),
                )
            ),
            # update the stock
            App.globalPut(self.Variables.stock, App.globalGet(self.Variables.stock)- Int(1)),
            Approve()
        ])

    def change_discount(self):
        return Seq([
            Assert(
                And(
                    # check if the size of transaction is 1
                    Global.group_size() == Int(1),
                    # check if the arguments passed is 2
                    Txn.application_args.length() == Int(2),
                    # check if the sender is the creator
                    Txn.sender() == App.globalGet(self.Variables.creator),

                ),
            ),
            App.globalPut(self.Variables.discount, Btoi(Txn.application_args[1])),
            Approve()
        ])

    def update_stock(self):
         return Seq([
            Assert(
                And(
                    # check if the size of transaction is 1
                    Global.group_size() == Int(1),
                    # check if the arguments passed is 2
                    Txn.application_args.length() == Int(2),
                    # make sure that arg passed is greater than 0
                    Btoi(Txn.application_args[1]) > Int(0),
                    # check if the sender is the creator
                    Txn.sender() == App.globalGet(self.Variables.creator),

                ),
            ),
            App.globalPut(self.Variables.stock, Btoi(Txn.application_args[1])),
            Approve()
        ])

    def application_deletion(self):
        return Return(Txn.sender() == Global.creator_address())

    def application_start(self):
        return Cond(
            [Txn.application_id() == Int(0), self.application_creation()],
            [Txn.on_completion() == OnComplete.DeleteApplication,
             self.application_deletion()],
            [Txn.application_args[0] == self.AppMethods.buy, self.buy()],
            [Txn.application_args[0] == self.AppMethods.change_discount, self.change_discount()],
            [Txn.application_args[0] == self.AppMethods.update_stock, self.update_stock()],
        )

    def approval_program(self):
        return self.application_start()

    def clear_program(self):
        return Return(Int(1))


import requests
from bs4 import BeautifulSoup
from lxml import html
import pandas as pd
import numpy as np
import nltk
import contractions
from nltk.stem import WordNetLemmatizer
from nltk import word_tokenize
from nltk.corpus import stopwords
from nltk.corpus import wordnet
from gensim.models import Word2Vec
import multiprocessing
#nltk.download('wordnet')
#nltk.download('averaged_perceptron_tagger')
import re
import swifter
from sklearn.model_selection import train_test_split
import seaborn as sns
from joblib import load

#getting the overarching categories
def get_categories(links):
    final = []
    for link in links:
        category = link.text
        url = link['href']
        if not url.startswith('http'):
            url = "https://www.theage.com.au"+url
        final.append([category, url])
    return final

#getting the urls of each of the articles in each category
def get_articles(final):
    all_articles = []
    for category, url in final:
        response = requests.get(url)
        soup = BeautifulSoup(response.content, 'html.parser')
        pagelinks = soup.find_all("a", {'data-test':"article-link"})
        for pagelink in pagelinks:
            url_1 = pagelink['href']
            if not url_1.startswith('http'):
                url_1 = "https://www.theage.com.au"+url_1
            all_articles.append([category, url_1])
    return all_articles

#opening each artile in each category and getting the full text
def get_text(all_articles):
    all_texts = []
    for cat, article in all_articles:
        response = requests.get(article)
        soup = BeautifulSoup(response.content, 'html.parser')
        texts = soup.find_all("p")
        full_text = ""
        for i in texts: #make into function? could speed up...
            text = i.text
            text = text + " "
            full_text += text
        all_texts.append(full_text)
    return all_texts

def string_normalise(string):
    string = re.sub('Credit:AP','', string)
    string = re.sub('Copyright © 2020','', string)
    string = re.sub('staff reporters','', string)
    string = re.sub('AP','', string)
    string = re.sub('\$','dollar ', string)
    string = re.sub('per cent','per-cent', string)
    string = re.sub(r"[^-\w\s']", ' ', string)
    #string = re.sub(r"[^-A-Za-z_\s]",' ', string)
    string = re.sub(r"[\d]",' ', string)
    string = re.sub(r"[']", '', string)
    string = string.lower()
    return string

def remove_stopwords(words):
    stopword_list = set(stopwords.words('english'))
    #Remove stop words from list of tokenized words
    new_words = [word for word in words if word not in stopword_list]
    return new_words

def get_wordnet_pos(word):
    #Map POS tag to first character lemmatize() accepts
    tag = nltk.pos_tag([word])[0][1][0].upper()
    tag_dict = {"J": wordnet.ADJ,
                "N": wordnet.NOUN,
                "V": wordnet.VERB,
                "R": wordnet.ADV}

    return tag_dict.get(tag, wordnet.NOUN)

def lemmatizer(list_of_words):
    lemma = WordNetLemmatizer()
    new_words = [lemma.lemmatize(word, get_wordnet_pos(word)) for word in list_of_words]
    return new_words

def create_corpus(links, update = True):
    final = get_categories(links)
    all_articles = get_articles(final)
    all_texts = get_text(all_articles)

    df = pd.DataFrame(all_articles, columns=['Categories', 'Url'])
    df['text'] = all_texts
    df = df.drop_duplicates(subset=None, keep="first").reset_index()
    df = df.drop(columns='index')

    df['processed_text'] = df.text.swifter.apply(string_normalise)
    df['processed_text'] = df.processed_text.swifter.apply(word_tokenize)
    df['processed_text'] = df.processed_text.swifter.apply(remove_stopwords)
    df['processed_text'] = df.processed_text.swifter.apply(lemmatizer)
    #add word count... and filter out low word counts

    if (update):
        dfAge = pd.read_csv('theage.csv')
        df = pd.concat([dfAge, df], ignore_index = True)
        df = df.drop_duplicates(subset=['Categories', 'Url'], keep="first").reset_index()
        df = df.drop(columns='index')
        df.to_csv('theage.csv', index = False)
        return df
    else:
        return df

def get_webpage(url):
    url = url
    response = requests.get(url)
    soup = BeautifulSoup(response.content, 'html.parser')

    categories = soup.find("nav",attrs={'class':'_3-9zZ'})
    links = categories.find_all("a")
    df = create_corpus(links)
    return df

def tfidf_rf_pred(lists):
    tfidf = load('tfidf_vectorizier.joblib')
    output = tfidf.transform(lists)
    return output

def get_individual_article(article):
    response = requests.get(article)
    soup = BeautifulSoup(response.content, 'html.parser')
    texts = soup.find_all("p")
    full_text = ""
    for i in texts:
        text = i.text
        text = text + " "
        full_text += text

    return full_text

def input_cleaner(text):
    processed_text = string_normalise(text)
    processed_text = word_tokenize(processed_text)
    processed_text = remove_stopwords(processed_text)
    processed_text = lemmatizer(processed_text)
    processed_text =  " ".join(processed_text)
    return processed_text


"""
This main script runs a simulation and saves the output to ./figures/

Example use case with animated output:
python main.py --num_iters 100 --plot 1

Example use case without animated output:
python main.py --num_iters 100 --plot 0
"""
import argparse
import os
import time

import matplotlib.pyplot as plt

import CAAF.model as model

parser = argparse.ArgumentParser()
parser.add_argument(
    "-n",
    "--num_iters",
    help="number of iterations to run the model",
    type=int,
    required=True,
)
parser.add_argument(
    "-p",
    "--plot",
    help="saves animation of simulation in a .mp4 file",
    type=int,
    default=0,
)
args = parser.parse_args()

num_iters = args.num_iters
plot = args.plot

parameters = {
    "row_size": 200,
    "col_size": 200,
    "refractory_period": 50,
    "driving_period": 220,
    "prob_not_fire": 0.05,
    "prob_con": 0.09,
    "prob_def": 0.05,
}

if __name__ == "__main__":

    heart = model.Heart(**parameters)
    os.makedirs('figures', exist_ok=True)

    start_time = time.time()
    heart.update(num_iters=num_iters, plot=plot)
    duration = time.time() - start_time
    print("Time: {0} s".format(duration))

    # Number of active cells with time
    fig, ax = plt.subplots(figsize=[5, 5])
    ax.plot(heart.num_active_cells)
    fig.savefig("./figures/num_active_cells.png")


import os

alphabet = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
            'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
            'y', 'z')

move = 1

with open('text.txt', 'w') as infile:
    for _ in range(4):
        infile.write('Hello' + '\n')

outfile = open('cipher_text.txt', 'w')

with open('text.txt', 'r') as infile:
    for string in infile:
        new_line = str()
        for sym in list(string[:-1]):
            if sym == sym.upper():
                new_index = alphabet.index(sym.lower()) + move
                new_line += str(alphabet[new_index]).upper()
            else:
                new_index = alphabet.index(sym) + move
                new_line += str(alphabet[new_index])
        move += 1
        outfile.write(new_line + '\n')

outfile.close()