from __future__ import annotations
from typing import Any, Dict, Set, Optional, Union, Type, Callable

import optuna
import copy
import warnings
import logging
import importlib
from data.protac_dataloader import PROTACDataModule
from models.wrapper_model import WrapperModel, ProtacModel # noqa: F401

from hyperparameter_tuning.cli import TuneLightningCLI
from packaging import version
from optuna.storages._cached_storage import _CachedStorage
from optuna.storages._rdb.storage import RDBStorage
from pytorch_lightning import LightningModule, Trainer
from pytorch_lightning.callbacks import Callback

from jsonargparse import (
    ActionConfigFile,
    ArgumentParser,
    class_from_function,
    Namespace,
    register_unresolvable_import_paths,
    set_config_read_mode,
    CLI,
    lazy_instance,
)

def suggest_optuna_param(trial: optuna.Trial,
                          name: str,
                          config: Dict[str, Any]) -> Any:
    """Given a config, suggest a value for the hyperparameter.

    Args:
        trial (optuna.Trial): The current Optuna trial
        name (str): The name of the hyperparameter
        config (Dict[str, Any]): The configuration of the hyperparameter

    Returns:
        Any: Suggested hyperparameter value
    """
    return getattr(trial, config['function'])(name, **config['kwargs'])

def objective(trial: optuna.Trial, cli: Optional[TuneLightningCLI] = None, config: Optional[Dict[str, Any]] = None) -> float:
    """Optuna objective function. Creates a new TuneLightningCLI object with the suggested hyperparameters and then fits the newly instantiated model.

    Args:
        trial (optuna.Trial): Optuna trial
        cli (Optional[TuneLightningCLI], optional): Custom LightningCLI object. Defaults to None.
        config (Optional[Dict[str, Any]], optional): Extra additional configurations as parsed arguments to the custom CLI. Defaults to None.

    Returns:
        float: Value of the objective function
    """
    if cli is None and config is None:
        raise ValueError('Either cli or config must be provided')
    elif cli is None and config is not None:
        cli_trial = TuneLightningCLI(ProtacModel,
                                     PROTACDataModule,
                                     seed_everything_default=42,
                                     parser_kwargs={'parser_mode': 'omegaconf'},
                                     args=config,
                                     run=False)
    else:
        cli_trial = copy.deepcopy(cli)
    if config is not None:
        print('WARNING. Both cli and config are provided. CLI "optuna" config will be updated.')
        cli_trial.config['optuna'].update(config)
    # Change CLI config with Optuna suggested values
    for hparam, cfg in cli_trial.config['optuna']['hparams'].items():
        cli_trial.config[hparam] = suggest_optuna_param(trial, hparam, cfg)
    # Change logging name if TensorBoardLogger is used
    if 'trainer.logger.TensorBoardLogger' in cli_trial.config:
        name = cli_trial.config['trainer.logger.TensorBoardLogger.init_args.name']
        name = f'{name}_{trial.number}'
        cli_trial.config['trainer.logger.TensorBoardLogger.init_args.name'] = name
    # Store current configuration in the trial attributes
    trial.set_user_attr('config', cli_trial.config)
    # TODO: Change Trainer config `overfit_batches` if not there already, then
    # fit the model and check its performance. If train accuracy not close to
    # 100%, then return a bad score. Else, remove the `overfit_batches` config
    # and fit the model.
    #
    # Turn on `overfit_batches` (with 5% of the data) and fit the model
    cli_trial.config['trainer.overfit_batches'] = 1
    # cli_trial.config['trainer.limit_val_batches'] = 1.0
    cli_trial.instantiate_classes()
    cli_trial.trainer.fit(cli_trial.model, cli_trial.datamodule)
    # Check if train accuracy is close to 100%
    if cli_trial.trainer.callback_metrics['train_acc'] < 0.95:
        logging.warning(f'WARNING. Train accuracy is {cli_trial.trainer.callback_metrics["train_acc"]}. Returning bad score.')
        return 0.0
    # Turn off `overfit_batches` and fit the model
    cli_trial.config['trainer.overfit_batches'] = 0.0
    # NOTE: Instantiating again the classes would hopefully overwrite the model
    # and the dataloader, without wasting memory...
    # TODO: Add Optuna callback for pruning to Trainer callbacks
    # Instantiate classes and fit model
    cli_trial.instantiate_classes()
    cli_trial.trainer.fit(cli_trial.model, cli_trial.datamodule)
    # Obtain the metric value
    return cli_trial.trainer.callback_metrics[cli_trial.config.optuna['metric']]

# Define key names of `Trial.system_attrs`.
_PRUNED_KEY = "ddp_pl:pruned"
_EPOCH_KEY = "ddp_pl:epoch"

class CustomPyTorchLightningPruningCallback(Callback):
    """PyTorch Lightning callback to prune unpromising trials.

    See `the example <https://github.com/optuna/optuna-examples/blob/
    main/pytorch/pytorch_lightning_simple.py>`__
    if you want to add a pruning callback which observes accuracy.

    Args:
        trial:
            A :class:`~optuna.trial.Trial` corresponding to the current evaluation of the
            objective function.
        monitor:
            An evaluation metric for pruning, e.g., ``val_loss`` or
            ``val_acc``. The metrics are obtained from the returned dictionaries from e.g.
            ``pytorch_lightning.LightningModule.training_step`` or
            ``pytorch_lightning.LightningModule.validation_epoch_end`` and the names thus depend on
            how this dictionary is formatted.

    .. note::
        For the distributed data parallel training, the version of PyTorchLightning needs to be
        higher than or equal to v1.5.0. In addition, :class:`~optuna.study.Study` should be
        instantiated with RDB storage.
    """

    def __init__(self, trial: optuna.trial.Trial, monitor: str) -> None:
        super().__init__()

        self._trial = trial
        self.monitor = monitor
        self.is_ddp_backend = False

    def on_init_start(self, trainer: Trainer) -> None:
        self.is_ddp_backend = (
            trainer._accelerator_connector.distributed_backend is not None  # type: ignore
        )
        if self.is_ddp_backend:
            if version.parse(pl.__version__) < version.parse("1.5.0"):  # type: ignore
                raise ValueError("PyTorch Lightning>=1.5.0 is required in DDP.")
            if not (
                isinstance(self._trial.study._storage, _CachedStorage)
                and isinstance(self._trial.study._storage._backend, RDBStorage)
            ):
                raise ValueError(
                    "optuna.integration.PyTorchLightningPruningCallback"
                    " supports only optuna.storages.RDBStorage in DDP."
                )

    def on_validation_end(self, trainer: Trainer, pl_module: LightningModule) -> None:

        # When the trainer calls `on_validation_end` for sanity check,
        # do not call `trial.report` to avoid calling `trial.report` multiple times
        # at epoch 0. The related page is
        # https://github.com/PyTorchLightning/pytorch-lightning/issues/1391.
        if trainer.sanity_checking:
            return

        epoch = pl_module.current_epoch

        current_score = trainer.callback_metrics.get(self.monitor)
        if current_score is None:
            message = (
                "The metric '{}' is not in the evaluation logs for pruning. "
                "Please make sure you set the correct metric name.".format(self.monitor)
            )
            warnings.warn(message)
            return

        should_stop = False
        if trainer.is_global_zero:
            self._trial.report(current_score.item(), step=epoch)
            should_stop = self._trial.should_prune()
        # TODO: The following line breaks the current version of Pytorch
        # Lightning. But I suspect it's necessary in a distributed training
        # environment... so it shouldn't matter for us...
        # should_stop = trainer.training_type_plugin.broadcast(should_stop)
        trainer.should_stop = should_stop
        if not should_stop:
            return

        if not self.is_ddp_backend:
            message = "Trial was pruned at epoch {}.".format(epoch)
            raise optuna.TrialPruned(message)
        else:
            # Stop every DDP process if global rank 0 process decides to stop.
            trainer.should_stop = True
            if trainer.is_global_zero:
                self._trial.storage.set_trial_system_attr(self._trial._trial_id, _PRUNED_KEY, True)
                self._trial.storage.set_trial_system_attr(self._trial._trial_id, _EPOCH_KEY, epoch)

    def on_fit_end(self, trainer: Trainer, pl_module: LightningModule) -> None:
        if not self.is_ddp_backend:
            return

        # Because on_validation_end is executed in spawned processes,
        # _trial.report is necessary to update the memory in main process, not to update the RDB.
        _trial_id = self._trial._trial_id
        _study = self._trial.study
        _trial = _study._storage._backend.get_trial(_trial_id)  # type: ignore
        _trial_system_attrs = _study._storage.get_trial_system_attrs(_trial_id)
        is_pruned = _trial_system_attrs.get(_PRUNED_KEY)
        epoch = _trial_system_attrs.get(_EPOCH_KEY)
        intermediate_values = _trial.intermediate_values
        for step, value in intermediate_values.items():
            self._trial.report(value, step=step)

        if is_pruned:
            message = "Trial was pruned at epoch {}.".format(epoch)
            raise optuna.TrialPruned(message)


import numpy as np
import random
import json
from glob import glob
from keras.models import model_from_json,load_model
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import  ModelCheckpoint,Callback,LearningRateScheduler
import keras.backend as K
from model import Unet_model
from losses import *

class SGDLearningRateTracker(Callback):

    def on_epoch_begin(self, epoch, logs = {}):
        optimizer = self.model.optimizer
        lr = K.get_value(optimizer.lr)
        decay = K.get_value(optimizer.decay)
        lr = lr / 10
        decay = decay * 10
        K.set_value(optimizer.lr, lr)
        K.set_value(optimizer.decay, decay)
        print("lr changed to:", lr)
        print("decay changed to:", decay)

class Training(object):

    '''
    load a model
    input: string 'model_name': filepath to model and weights, not including extension
    output: model with loaded weights. can fit on model using loaded_model = True in fit_model method
    '''
    def load_model(self, model_name):
        '''
        Load a model
        INPUT  (1) string 'model_name': filepath to model and weights, not including extension
        OUTPUT: Model with loaded weights. can fit on model using loaded_model=True in fit_model method
        '''
        print ('Loading model {}'.format(model_name))
        model_toload = '/Users/kaitlinylim/Documents/tumorproj/models/{}.json'.format(model_name)
        weights = '/Users/kaitlinylim/Documents/tumorproj/weights/{}.hdf5'.format(model_name)
        with open(model_toload) as f:
            loaded_model_json = f.read()
        model_comp = model_from_json(loaded_model_json)
        model_comp.load_weights(weights)
        print ('model loaded.')
        model_comp.compile(loss = gen_dice_loss, optimizer = SGD(lr = 0.08, momentum = 0.9, decay = 5e-6, nesterov = False),
                           metrics = [dice_whole_metric, dice_core_metric, dice_en_metric])
        print('model compiled')
        self.model = model_comp
        return model_comp

    # constructor
    def __init__(self, batch_size, nb_epoch, load_model_resume_training = None):
        self.batch_size = batch_size
        self.nb_epoch = nb_epoch

        #loading model from path to resume previous training without recompiling the whole model
        if load_model_resume_training is not None:
            # self.model = self.load_model(load_model_resume_training,custom_objects={'gen_dice_loss': gen_dice_loss,'dice_whole_metric':dice_whole_metric,'dice_core_metric':dice_core_metric,'dice_en_metric':dice_en_metric})
            self.model = self.load_model(load_model_resume_training)
            print("pre-trained model loaded!")
        else:
            unet = Unet_model(img_shape = (128, 128, 4))
            self.model = unet.model
            print("u-net cnn compiled!")

    def fit_unet(self, X33_train, Y_train, X_patches_valid = None, Y_labels_valid = None):
        train_generator = self.img_msk_gen(X33_train, Y_train, 9999)
        checkpointer = ModelCheckpoint(filepath = "/Users/kaitlinylim/Documents/tumorproj/checkpoints/ResUnet.{epoch:02d}_{val_loss:.3f}.hdf5",
                                      verbose = 1)
        self.model.fit_generator(train_generator, steps_per_epoch = len(X33_train)//self.batch_size,
                                epochs = self.nb_epoch, validation_data = (X_patches_valid, Y_labels_valid),
                                verbose = 1, callbacks = [checkpointer, SGDLearningRateTracker()])

    '''
    a custom generator that performs data augmentation on both patches and their corresponding masks
    '''
    def img_msk_gen(self, X33_train, Y_train, seed):
        datagen = ImageDataGenerator(horizontal_flip = True, data_format = 'channels_last')
        datagen_msk = ImageDataGenerator(horizontal_flip = True, data_format = 'channels_last')
        image_generator = datagen.flow(X33_train, batch_size = 4, seed = seed)
        y_generator = datagen_msk.flow(Y_train, batch_size = 4, seed = seed)
        while True:
            yield(image_generator.next(), y_generator.next())

    '''
    input: string 'model_name': path where to save model and weights without extension
    saves current model as json and weights as h5df file
    '''
    def save_model(self, model_name):
        model_tosave = '{}.json'.format(model_name)
        json_string = self.model.to_json()
        with open('/Users/kaitlinylim/Documents/tumorproj/models/'+model_tosave, 'w') as json_file:
            json_file.write(json_string)
            weights = '{}.hdf5'.format(model_name)
            self.model.save_weights('/Users/kaitlinylim/Documents/tumorproj/weights/'+weights)
        print ('model saved')

if __name__ == '__main__':

    X_patches = np.load('/Users/kaitlinylim/Documents/tumorproj/numpy/X_patches.npy')
    Y_labels = np.load('/Users/kaitlinylim/Documents/tumorproj/numpy/Y_labels.npy')
    print('loading patches done\n')

    # split the training data further into training data and validation data
    div = int(X_patches.shape[0] * 0.8)
    X_training = X_patches[0:div]
    X_validation = X_patches[div:]
    Y_training = Y_labels[0:div]
    Y_validation = Y_labels[div:]

    brain_seg_1 = Training(batch_size = 4, nb_epoch = 2)

    print("number of trainable parameters:", brain_seg_1.model.count_params())
    # print(brain_seg_1.model.summary())

    brain_seg_1.fit_unet(X_training, Y_training, X_validation, Y_validation)

    # if an already trained model is being loaded
    model_to_load = 'seg_2_epochs'
    brain_seg_2 = Training(batch_size = 4, nb_epoch = 2, load_model_resume_training = model_to_load)
    brain_seg_2.fit_unet(X_training, Y_training, X_validation, Y_validation)
    brain_seg_2.save_model('seg_4_epochs')


import os
import logging
import appdirs

from typing import Optional

logger = logging.getLogger(__name__)

class LocalFSCaching:

    def __init__(self):
        self._cache_dir = appdirs.user_cache_dir()
        logger.debug("Use '%s' as cache dir", self._cache_dir)

    def _get_cache_file_path(self, key: str, entity: str) -> str:
        file_path = os.path.join(
            self._cache_dir,
            "kobo2anki",
            entity,
            key
        )
        logger.debug(
            "Using cache path %s for key %s and entity %s", file_path,
            key, entity
        )
        return file_path

    def get_cached_data(self, entity: str, key: str) -> Optional[bytes]:
        result = None  # type: Optional[bytes]
        cache_file_path = self._get_cache_file_path(key, entity)
        try:
            with open(cache_file_path, 'rb') as fh:
                result = fh.read()
        except FileNotFoundError:
            logger.debug("Cache for %s with key %s not found", key, entity)
        return result

    def save_data_to_cache(self, entity: str, key: str, data: bytes):
        cache_file_path = self._get_cache_file_path(key, entity)
        if os.path.exists(cache_file_path):
            logger.warning(
                """Looks like we already have some cache fo key %s
                and entity %s. Will overwrite it.""",
                key, entity
            )
        else:
            cache_dir = os.path.dirname(cache_file_path)
            if not os.path.exists(cache_dir):
                logger.debug("Going to create cache dir '%s'", cache_dir)
                os.makedirs(cache_dir)

        with open(cache_file_path, "wb") as fh:
            fh.write(data)
        logger.debug("Recorded %d bytes into cache file %s", len(data), cache_file_path)


from src.models.model import Encoder, Decoder, Discriminator
from src.trainer import Trainer
from src.dataset import MNISTToSVHN
from torch.utils.data import DataLoader

import os

mnist_to_svhn_dataset = MNISTToSVHN(
    mnist_img_dir=os.path.abspath("./mnist"),
    svhn_img_dir=os.path.abspath("./svhn"),
)
data_loader = DataLoader(mnist_to_svhn_dataset, batch_size=10, shuffle=True)
trainer = Trainer(
    encoder=Encoder(),
    decoder=Decoder(),
    discriminator=Discriminator(),
    data_loader=data_loader,
    a=100,
    b=1,
    c=0.05
)
trainer.train(100)


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import tempfile
import numpy as np
from tensor2tensor.data_generators import video_generated  # pylint: disable=unused-import
from tensor2tensor.models.video import next_frame_glow
from tensor2tensor.utils import registry
import tensorflow.compat.v1 as tf
from tensorflow.compat.v1 import estimator as tf_estimator
MODES = tf_estimator.ModeKeys

# TODO(mechcoder): Refactor or merge tests with the other next_frame_tests when
# this moves to a public version.
def fill_hparams(hparams, in_frames, out_frames, gen_mode="conditional",
                 latent_dist_encoder="pointwise", pretrain_steps=-1,
                 num_train_frames=-1, cond_first_frame=False,
                 apply_dilations=False, activation="relu"):
  """Set next_frame_glow hparams."""
  hparams.latent_activation = activation
  hparams.latent_apply_dilations = apply_dilations
  hparams.video_num_input_frames = in_frames
  hparams.video_num_target_frames = out_frames
  hparams.latent_dist_encoder = latent_dist_encoder
  hparams.gen_mode = gen_mode
  hparams.pretrain_steps = pretrain_steps
  hparams.num_train_frames = num_train_frames
  hparams.cond_first_frame = cond_first_frame
  if latent_dist_encoder in ["conv_net", "conv3d_net"]:
    hparams.num_cond_latents = in_frames
  else:
    hparams.num_cond_latents = 1
  problem = registry.problem("video_stochastic_shapes10k")
  p_hparams = problem.get_hparams(hparams)
  hparams.problem = problem
  hparams.problem_hparams = p_hparams
  hparams.tiny_mode = True
  hparams.reward_prediction = False
  hparams.latent_architecture = "glow_resnet"
  hparams.latent_encoder_depth = 2
  hparams.latent_pre_output_channels = 32
  if (hparams.gen_mode == "conditional" and
      hparams.latent_dist_encoder == "pointwise"):
    hparams.batch_size = 16
    hparams.init_batch_size = 16
  else:
    hparams.batch_size = 16
    hparams.init_batch_size = 32
  hparams.affine_coupling_width = 32
  hparams.depth = 5
  hparams.n_levels = 2
  return hparams

def fill_infer_targets(x):
  x["infer_targets"] = tf.identity(x["targets"])
  return x

def create_basic_features(hparams):
  dataset = hparams.problem.dataset(MODES.TRAIN, hparams=hparams)
  dataset = dataset.batch(hparams.batch_size)
  dataset = dataset.map(fill_infer_targets)
  return dataset.make_one_shot_iterator().get_next()

class NextFrameGlowTest(tf.test.TestCase):
  """Utils for testing next_frame_glow."""

  def should_run_session(self, hparams):
    # dilated conv-3d not available on CPU.
    return tf.test.is_gpu_available() or not hparams.latent_apply_dilations

  def checkAllConds(self, conds_array, num_total_frames, hparams):
    if hparams.cond_first_frame:
      self.assertEqual(conds_array, [True]*(num_total_frames + 1))
    elif hparams.pretrain_steps > -1:
      self.assertEqual(conds_array, [False]*num_total_frames)
    elif hparams.latent_dist_encoder != "pointwise":
      self.assertEqual(conds_array, [True]*num_total_frames)

  def RunModel(self, model, train_op, hparams, features, num_frames,
               model_path=None):
    exp_num_frames = num_frames + int(hparams.cond_first_frame)
    if hparams.gen_mode == "conditional":
      self.assertLen(model.all_top_latents, exp_num_frames)
      self.assertLen(model.all_level_latents, exp_num_frames)

    with tf.Session() as session:

      if model_path is not None:
        saver = tf.train.Saver()

      session.run(tf.global_variables_initializer())

      # Run initialization.
      init_op = tf.get_collection("glow_init_op")
      session.run(init_op)

      loss, top_conds = session.run([train_op["training"], model._all_conds])  # pylint: disable=protected-access
      self.checkAllConds(top_conds, num_frames, hparams)

      if model_path is not None:
        saver.save(session, model_path)

      # Check that one forward-propagation does not NaN, i.e
      # initialization etc works as expected.
      self.assertTrue(loss > 0.0 and loss < 10.0)

  def GlowTrainAndDecode(self, in_frames=1, out_frames=1,
                         latent_dist_encoder="pointwise",
                         gen_mode="conditional", pretrain_steps=-1,
                         num_train_frames=-1, cond_first_frame=False,
                         apply_dilations=False, activation="relu"):
    """Test 1 forward pass and sampling gives reasonable results."""
    if num_train_frames == -1:
      total_frames = in_frames + out_frames
    else:
      total_frames = num_train_frames

    curr_dir = tempfile.mkdtemp()
    model_path = os.path.join(curr_dir, "model")

    # Training pipeline
    with tf.Graph().as_default():
      hparams = next_frame_glow.next_frame_glow_hparams()
      hparams = fill_hparams(hparams, in_frames, out_frames,
                             gen_mode, latent_dist_encoder, pretrain_steps,
                             num_train_frames, cond_first_frame,
                             apply_dilations, activation)
      features = create_basic_features(hparams)
      model = next_frame_glow.NextFrameGlow(hparams, MODES.TRAIN)
      _, train_op = model(features)
      if self.should_run_session(hparams):
        self.RunModel(model, train_op, hparams, features, total_frames,
                      model_path)

    # Inference pipeline
    with tf.Graph().as_default():
      hparams = next_frame_glow.next_frame_glow_hparams()
      if hparams.gen_mode == "unconditional":
        hparams.video_num_target_frames = 1
      hparams = fill_hparams(hparams, in_frames, out_frames,
                             gen_mode, latent_dist_encoder, pretrain_steps,
                             num_train_frames, cond_first_frame,
                             apply_dilations, activation)
      features = create_basic_features(hparams)
      model = next_frame_glow.NextFrameGlow(
          hparams, tf_estimator.ModeKeys.PREDICT)
      predictions = model.infer(features)
      outputs = predictions["outputs"]
      model_path = os.path.join(curr_dir, "model")

      if self.should_run_session(hparams):
        with tf.Session() as session:
          saver = tf.train.Saver()
          saver.restore(session, model_path)
          outputs_np = session.run(outputs)
          self.assertEqual(outputs_np.shape, (16, out_frames, 64, 64, 3))
          self.assertTrue(np.all(outputs_np <= 255))
          self.assertTrue(np.all(outputs_np >= 0))

if __name__ == "__main__":
  tf.test.main()


""" Full assembly of the parts to form the complete network """

import torch.nn.functional as F
import torch

from .unet_parts import *

class UNet(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(UNet, self).__init__()
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.inc = inconv(in_channels, 64)
        self.down1 = down(64, 128)
        self.down2 = down(128, 256)
        self.down3 = down(256, 512)
        self.down4 = down(512, 512)
        self.up1 = up(1024, 256)
        self.up2 = up(512, 128)
        self.up3 = up(256, 64)
        self.up4 = up(128, 64)
        self.outc = outconv(64, out_channels)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                # n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                # m.weight.data.normal_(0, math.sqrt(2. / n))
                torch.nn.init.kaiming_normal_(m.weight)
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    def forward(self, x):
        x1 = self.inc(x)
        x2 = self.down1(x1)
        x3 = self.down2(x2)
        x4 = self.down3(x3)
        x5 = self.down4(x4)
        x = self.up1(x5, x4)
        x = self.up2(x, x3)
        x = self.up3(x, x2)
        x = self.up4(x, x1)
        x = self.outc(x)
        return F.relu(x)


import random
import time
from datetime import datetime, timedelta
from enum import Enum
from game.ai.nation_ai import NationAI

from random_functions import random_functions

class EconomicState(Enum):
    RECESSION = 1
    DEPRESSION = 2
    EXPANSION = 3
    RECOVERY = 4

leaders = {
    "1910": "Emperor Xuantong",
    "1914": "Yuan Shikai",
    "1918": "Xu Shichangi",
    "1932": "Lin Sen",
    "1936": "Lin Sen",
    "1939": "Lin Sen"
}
monarchs = {
    "1910": "Emperor Xuantong",
    "1914": "Emperor Xuantong",
    "1918": "Emperor Xuantong",
    "1932": "Emperor Xuantong",
    "1936": "Emperor Xuantong",
    "1939": "Emperor Xuantong"
}

population = {
    "1910": 422250794,
    "1914": 438552995,
    "1918": 457741400,
    "1932": 494506632,
    "1936": 506322173,
    "1939": 514761109
}
gdp = {
    "1910": 17340421053,
    "1914": 19231073684,
    "1918": 23090862632,
    "1932": 48070526316,
    "1936": 60420631579,
    "1939": 54934157895
}

class ChinaAI(NationAI):
    def __init__(self, globe):
        super().__init__(globe)
        self.region = "asia"
        self.name = "Republic of China"
        # social variables
        """population"""
        self.population = population[str(globe.date.year)]
        # political
        self.leader = leaders[str(globe.date.year)]
        self.political_power = 200
        self.political_exponent = 1.56
        """Stability"""
        self.stability = 95.56
        # economic
        self.corporate_taxes = 24.00
        self.income_taxes = 20.00
        self.current_gdp = gdp[str(globe.date.year)]
        """Components of GDP"""
        self.consumer_spending = 200
        self.investment = 300
        self.government_spending = 350
        self.exports = 1000
        self.imports = 1200
        """Economic Stimulus components"""
        self.economic_stimulus = False
        # military
        # international
        self.alliance = ""
        self.us_relations = 34.56
        # other

    # main function
    def main(self, globe):
        while self.population > 5000000:
            super().check_economic_state()
            super().check_population_growth()
            # random_functions.random_functions(self, globe)
            super().stability_happiness_change(globe)
            self.stability_happiness_change(globe)
            self.date += timedelta(days=1)
            break


from __future__ import absolute_import, division, print_function
__metaclass__ = type

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['stableinterface'],
                    'supported_by': 'core'}

DOCUMENTATION = r'''
---
module: copy
version_added: "historical"
short_description: Copies files to remote locations
description:
    - The C(copy) module copies a file from the local or remote machine to a location on the remote machine.
      Use the M(fetch) module to copy files from remote locations to the local box.
      If you need variable interpolation in copied files, use the M(template) module.
    - For Windows targets, use the M(win_copy) module instead.
options:
  src:
    description:
      - Local path to a file to copy to the remote server; can be absolute or relative.
        If path is a directory, it is copied recursively. In this case, if path ends
        with "/", only inside contents of that directory are copied to destination.
        Otherwise, if it does not end with "/", the directory itself with all contents
        is copied. This behavior is similar to Rsync.
  content:
    description:
      - When used instead of I(src), sets the contents of a file directly to the specified value.
        For anything advanced or with formatting also look at the template module.
    version_added: "1.1"
  dest:
    description:
      - 'Remote absolute path where the file should be copied to. If I(src) is a directory, this must be a directory too.
        If I(dest) is a nonexistent path and if either I(dest) ends with "/" or I(src) is a directory, I(dest) is created.
        If I(src) and I(dest) are files, the parent directory of I(dest) isn''t created: the task fails if it doesn''t already exist.'
    required: yes
  backup:
    description:
      - Create a backup file including the timestamp information so you can get
        the original file back if you somehow clobbered it incorrectly.
    type: bool
    default: 'no'
    version_added: "0.7"
  force:
    description:
      - the default is C(yes), which will replace the remote file when contents
        are different than the source. If C(no), the file will only be transferred
        if the destination does not exist.
    type: bool
    default: 'yes'
    aliases: [ thirsty ]
    version_added: "1.1"
  mode:
    description:
      - "Mode the file or directory should be. For those used to I(/usr/bin/chmod) remember that
        modes are actually octal numbers.  You must either specify the leading zero so that
        Ansible's YAML parser knows it is an octal number (like C(0644) or C(01777)) or quote it
        (like C('644') or C('0644') so Ansible receives a string and can do its own conversion from
        string into number.  Giving Ansible a number without following one of these rules will end
        up with a decimal number which will have unexpected results.  As of version 1.8, the mode
        may be specified as a symbolic mode (for example, C(u+rwx) or C(u=rw,g=r,o=r)).  As of
        version 2.3, the mode may also be the special string C(preserve).  C(preserve) means that
        the file will be given the same permissions as the source file."
  directory_mode:
    description:
      - When doing a recursive copy set the mode for the directories. If this is not set we will use the system
        defaults. The mode is only set on directories which are newly created, and will not affect those that
        already existed.
    version_added: "1.5"
  remote_src:
    description:
      - If C(no), it will search for I(src) at originating/master machine.
      - If C(yes) it will go to the remote/target machine for the I(src). Default is C(no).
      - Currently I(remote_src) does not support recursive copying.
      - I(remote_src) only works with C(mode=preserve) as of version 2.6.
    type: bool
    default: 'no'
    version_added: "2.0"
  follow:
    description:
      - This flag indicates that filesystem links in the destination, if they exist, should be followed.
    type: bool
    default: 'no'
    version_added: "1.8"
  local_follow:
    description:
      - This flag indicates that filesystem links in the source tree, if they exist, should be followed.
    type: bool
    default: 'yes'
    version_added: "2.4"
  checksum:
    description:
      - SHA1 checksum of the file being transferred. Used to validate that the copy of the file was successful.
      - If this is not provided, ansible will use the local calculated checksum of the src file.
    version_added: '2.5'
extends_documentation_fragment:
    - files
    - validate
    - decrypt
author:
    - Ansible Core Team
    - Michael DeHaan
notes:
   - The M(copy) module recursively copy facility does not scale to lots (>hundreds) of files.
     For alternative, see M(synchronize) module, which is a wrapper around C(rsync).
   - For Windows targets, use the M(win_copy) module instead.
'''

EXAMPLES = r'''
- name: example copying file with owner and permissions
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: 0644

- name: The same example as above, but using a symbolic mode equivalent to 0644
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: u=rw,g=r,o=r

- name: Another symbolic mode example, adding some permissions and removing others
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: u+rw,g-wx,o-rwx

- name: Copy a new "ntp.conf file into place, backing up the original if it differs from the copied version
  copy:
    src: /mine/ntp.conf
    dest: /etc/ntp.conf
    owner: root
    group: root
    mode: 0644
    backup: yes

- name: Copy a new "sudoers" file into place, after passing validation with visudo
  copy:
    src: /mine/sudoers
    dest: /etc/sudoers
    validate: /usr/sbin/visudo -cf %s

- name: Copy a "sudoers" file on the remote machine for editing
  copy:
    src: /etc/sudoers
    dest: /etc/sudoers.edit
    remote_src: yes
    validate: /usr/sbin/visudo -cf %s

- name: Copy using the 'content' for inline data
  copy:
    content: '# This file was moved to /etc/other.conf'
    dest: /etc/mine.conf'
'''

RETURN = r'''
dest:
    description: destination file/path
    returned: success
    type: string
    sample: /path/to/file.txt
src:
    description: source file used for the copy on the target machine
    returned: changed
    type: string
    sample: /home/httpd/.ansible/tmp/ansible-tmp-1423796390.97-147729857856000/source
md5sum:
    description: md5 checksum of the file after running copy
    returned: when supported
    type: string
    sample: 2a5aeecc61dc98c4d780b14b330e3282
checksum:
    description: sha1 checksum of the file after running copy
    returned: success
    type: string
    sample: 6e642bb8dd5c2e027bf21dd923337cbb4214f827
backup_file:
    description: name of backup file created
    returned: changed and if backup=yes
    type: string
    sample: /path/to/file.txt.2015-02-12@22:09~
gid:
    description: group id of the file, after execution
    returned: success
    type: int
    sample: 100
group:
    description: group of the file, after execution
    returned: success
    type: string
    sample: httpd
owner:
    description: owner of the file, after execution
    returned: success
    type: string
    sample: httpd
uid:
    description: owner id of the file, after execution
    returned: success
    type: int
    sample: 100
mode:
    description: permissions of the target, after execution
    returned: success
    type: string
    sample: 0644
size:
    description: size of the target, after execution
    returned: success
    type: int
    sample: 1220
state:
    description: state of the target, after execution
    returned: success
    type: string
    sample: file
'''

import os
import os.path
import shutil
import stat
import errno
import tempfile
import traceback

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_bytes, to_native

class AnsibleModuleError(Exception):
    def __init__(self, results):
        self.results = results

def split_pre_existing_dir(dirname):
    '''
    Return the first pre-existing directory and a list of the new directories that will be created.
    '''
    head, tail = os.path.split(dirname)
    b_head = to_bytes(head, errors='surrogate_or_strict')
    if head == '':
        return ('.', [tail])
    if not os.path.exists(b_head):
        if head == '/':
            raise AnsibleModuleError(results={'msg': "The '/' directory doesn't exist on this machine."})
        (pre_existing_dir, new_directory_list) = split_pre_existing_dir(head)
    else:
        return (head, [tail])
    new_directory_list.append(tail)
    return (pre_existing_dir, new_directory_list)

def adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed):
    '''
    Walk the new directories list and make sure that permissions are as we would expect
    '''

    if new_directory_list:
        working_dir = os.path.join(pre_existing_dir, new_directory_list.pop(0))
        directory_args['path'] = working_dir
        changed = module.set_fs_attributes_if_different(directory_args, changed)
        changed = adjust_recursive_directory_permissions(working_dir, new_directory_list, module, directory_args, changed)
    return changed

def main():

    module = AnsibleModule(
        # not checking because of daisy chain to file module
        argument_spec=dict(
            src=dict(type='path'),
            _original_basename=dict(type='str'),  # used to handle 'dest is a directory' via template, a slight hack
            content=dict(type='str', no_log=True),
            dest=dict(type='path', required=True),
            backup=dict(type='bool', default=False),
            force=dict(type='bool', default=True, aliases=['thirsty']),
            validate=dict(type='str'),
            directory_mode=dict(type='raw'),
            remote_src=dict(type='bool'),
            local_follow=dict(type='bool'),
            checksum=dict(),
        ),
        add_file_common_args=True,
        supports_check_mode=True,
    )

    src = module.params['src']
    b_src = to_bytes(src, errors='surrogate_or_strict')
    dest = module.params['dest']
    # Make sure we always have a directory component for later processing
    if os.path.sep not in dest:
        dest = '.{0}{1}'.format(os.path.sep, dest)
    b_dest = to_bytes(dest, errors='surrogate_or_strict')
    backup = module.params['backup']
    force = module.params['force']
    _original_basename = module.params.get('_original_basename', None)
    validate = module.params.get('validate', None)
    follow = module.params['follow']
    mode = module.params['mode']
    owner = module.params['owner']
    group = module.params['group']
    remote_src = module.params['remote_src']
    checksum = module.params['checksum']

    if not os.path.exists(b_src):
        module.fail_json(msg="Source %s not found" % (src))
    if not os.access(b_src, os.R_OK):
        module.fail_json(msg="Source %s not readable" % (src))
    if os.path.isdir(b_src):
        module.fail_json(msg="Remote copy does not support recursive copy of directory: %s" % (src))

    # Preserve is usually handled in the action plugin but mode + remote_src has to be done on the
    # remote host
    if module.params['mode'] == 'preserve':
        module.params['mode'] = '0%03o' % stat.S_IMODE(os.stat(b_src).st_mode)
    mode = module.params['mode']

    checksum_src = module.sha1(src)
    checksum_dest = None
    # Backwards compat only.  This will be None in FIPS mode
    try:
        md5sum_src = module.md5(src)
    except ValueError:
        md5sum_src = None

    changed = False

    if checksum and checksum_src != checksum:
        module.fail_json(
            msg='Copied file does not match the expected checksum. Transfer failed.',
            checksum=checksum_src,
            expected_checksum=checksum
        )

    # Special handling for recursive copy - create intermediate dirs
    if _original_basename and dest.endswith(os.sep):
        dest = os.path.join(dest, _original_basename)
        b_dest = to_bytes(dest, errors='surrogate_or_strict')
        dirname = os.path.dirname(dest)
        b_dirname = to_bytes(dirname, errors='surrogate_or_strict')
        if not os.path.exists(b_dirname):
            try:
                (pre_existing_dir, new_directory_list) = split_pre_existing_dir(dirname)
            except AnsibleModuleError as e:
                e.result['msg'] += ' Could not copy to {0}'.format(dest)
                module.fail_json(**e.results)

            os.makedirs(b_dirname)
            directory_args = module.load_file_common_arguments(module.params)
            directory_mode = module.params["directory_mode"]
            if directory_mode is not None:
                directory_args['mode'] = directory_mode
            else:
                directory_args['mode'] = None
            adjust_recursive_directory_permissions(pre_existing_dir, new_directory_list, module, directory_args, changed)

    if os.path.isdir(b_dest):
        basename = os.path.basename(src)
        if _original_basename:
            basename = _original_basename
        dest = os.path.join(dest, basename)
        b_dest = to_bytes(dest, errors='surrogate_or_strict')

    if os.path.exists(b_dest):
        if os.path.islink(b_dest) and follow:
            b_dest = os.path.realpath(b_dest)
            dest = to_native(b_dest, errors='surrogate_or_strict')
        if not force:
            module.exit_json(msg="file already exists", src=src, dest=dest, changed=False)
        if os.access(b_dest, os.R_OK):
            checksum_dest = module.sha1(dest)
    else:
        if not os.path.exists(os.path.dirname(b_dest)):
            try:
                # os.path.exists() can return false in some
                # circumstances where the directory does not have
                # the execute bit for the current user set, in
                # which case the stat() call will raise an OSError
                os.stat(os.path.dirname(b_dest))
            except OSError as e:
                if "permission denied" in to_native(e).lower():
                    module.fail_json(msg="Destination directory %s is not accessible" % (os.path.dirname(dest)))
            module.fail_json(msg="Destination directory %s does not exist" % (os.path.dirname(dest)))

    if not os.access(os.path.dirname(b_dest), os.W_OK) and not module.params['unsafe_writes']:
        module.fail_json(msg="Destination %s not writable" % (os.path.dirname(dest)))

    backup_file = None
    if checksum_src != checksum_dest or os.path.islink(b_dest):
        if not module.check_mode:
            try:
                if backup:
                    if os.path.exists(b_dest):
                        backup_file = module.backup_local(dest)
                # allow for conversion from symlink.
                if os.path.islink(b_dest):
                    os.unlink(b_dest)
                    open(b_dest, 'w').close()
                if validate:
                    # if we have a mode, make sure we set it on the temporary
                    # file source as some validations may require it
                    if mode is not None:
                        module.set_mode_if_different(src, mode, False)
                    if owner is not None:
                        module.set_owner_if_different(src, owner, False)
                    if group is not None:
                        module.set_group_if_different(src, group, False)
                    if "%s" not in validate:
                        module.fail_json(msg="validate must contain %%s: %s" % (validate))
                    (rc, out, err) = module.run_command(validate % src)
                    if rc != 0:
                        module.fail_json(msg="failed to validate", exit_status=rc, stdout=out, stderr=err)
                b_mysrc = b_src
                if remote_src:
                    _, b_mysrc = tempfile.mkstemp(dir=os.path.dirname(b_dest))

                    shutil.copyfile(b_src, b_mysrc)
                    try:
                        shutil.copystat(b_src, b_mysrc)
                    except OSError as err:
                        if err.errno == errno.ENOSYS and mode == "preserve":
                            module.warn("Unable to copy stats {0}".format(to_native(b_src)))
                        else:
                            raise
                module.atomic_move(b_mysrc, dest, unsafe_writes=module.params['unsafe_writes'])
            except (IOError, OSError):
                module.fail_json(msg="failed to copy: %s to %s" % (src, dest), traceback=traceback.format_exc())
        changed = True
    else:
        changed = False

    res_args = dict(
        dest=dest, src=src, md5sum=md5sum_src, checksum=checksum_src, changed=changed
    )
    if backup_file:
        res_args['backup_file'] = backup_file

    module.params['dest'] = dest
    if not module.check_mode:
        file_args = module.load_file_common_arguments(module.params)
        res_args['changed'] = module.set_fs_attributes_if_different(file_args, res_args['changed'])

    module.exit_json(**res_args)

if __name__ == '__main__':
    main()


import pygame, random, importlib
import concurrent.futures
import asyncio
from texture import *
from tunnelelement import *
from inline import *

"""
The tunnel managing class controlling the behavior of the tunnel elements
Capsuled within is the Tunnel Element initiator class handling the initialization of
new Tunnel Elements and thereby guiding the appearance of the tunnel
"""
class Tunnel:
    maxelements = 8

    #initialization of the tunnel manager
    def __init__(self, maincontroller):
        self.maxelements = Tunnel.maxelements
        #the elements are initialized as an empty array that is populated with Elements at a later time
        self.elements = []
        #upward reference to the maincontroller
        self.maincontroller = maincontroller
        #initialization of the ElementInitiator
        self.initiator = ElementInitiator(self)

    #act method inducing the initiators and elements behavior
    def act(self):
        self.initiator.act()
        for element in self.elements:
            element.act()

    """
    draw method
    calls draw method on all elements
    then blits their individual surfaces to the screen in the according order
    screenspace is taken into account while blitting
    each element is only drawn within the bounds of the next higher elements inline
    the rest is not supposed to be visible anyways and blitting it takes
    a considerable amount of time
    """
    def draw(self, screen):
        #array of each elements surface and the rect bounding their inline
        surfacedata = [ element.draw() for element in self.elements ]
        maxindex = len(surfacedata) - 1
        index = 0
        #loop blitting each element within the bound of the inline of the next
        while index < maxindex:
            screen.blit(surfacedata[index][0], surfacedata[index + 1][1], surfacedata[index + 1][1])
            index += 1
        #blitting the last element to the whole screen if there is at least one element
        if maxindex >= 0:
            screen.blit(surfacedata[index][0], (0,0))

"""
Multi Threaded Implementation attempt of the Tunnel Class
Child class of the tunnel class, only implements threaded versions of act and draw
No longer in use as the improved implementation is as the name suggests better
Only for redundancy
"""
class MultiThreadedTunnel(Tunnel):
    def act(self):
        self.initiator.act()
        with concurrent.futures.ThreadPoolExecutor() as executor:
            futures = [None for _ in range(len(self.elements))]
            for index, element in enumerate(self.elements):
                futures[index] = executor.submit(element.act)
            concurrent.futures.wait(futures)

    def draw(self, screen):
        with concurrent.futures.ThreadPoolExecutor() as executor:
            futures = [None for _ in range(len(self.elements))]
            for index, element in enumerate(self.elements):
                futures[index] = executor.submit(element.draw)
            for future in futures:
                screen.blit(future.result(), (0, 0))

"""
A more promising attempt at a MultiThreaded Implementation
Combines the draw and act method for the TunnelElements in one parallelizable function
To reduce the overhead caused by thread invocation
Problems:
The overhead is still greater than the cost
Ideas:
Maybe it's possible to implement the threads in form of an executor pool object
outside of a with block then they could be initialized at the start and only
filled with work to execute at runtime, which would eliminate the dragging overhead
Maybe the with block has to be in the global main function and the thread pool
executor has to be passed down as a reference to this object then it could
probably also behave in the same way
"""
class ImprovedMultiThreadedTunnel(MultiThreadedTunnel):
    #parallelizable function which runs both an elements act and draw method
    #returns the return value of the element.draw call
    def drawandact(self, element, screen):
        element.act()
        return element.draw()

    #act metod only inducing the initiators act method because element act is
    #induced in the draw method
    def act(self):
        self.initiator.act()

    #MultiThreaded draw method
    #A thread is initialized for each Element and is filled with the drawandact
    #method for each element reference
    #then results are collected in order and
    def draw(self, screen):
        #initialization of the thread pool
        with concurrent.futures.ThreadPoolExecutor() as executor:
            #populates a list with futuire objects each containing the return value
            #of a threads function call at a later point
            futures = [None for _ in range(len(self.elements))]
            for index, element in enumerate(self.elements):
                #call the drawandact function for each element
                futures[index] = executor.submit(self.drawandact, element, screen)
            results = []
            #await results and draw the surfaces
            #for description of how screenspace blitting is implemented see the
            #documentation of the draw method in the base Tunnel class
            for index, future in enumerate(futures):
                results.append(future.result())
                if index > 0:
                    screen.blit(results[index - 1][0], results[index][1], results[index][1])
            if len(results) > 0:
                screen.blit(results[-1][0], (0,0))

"""
Element initiator class
initializes the Tunnel Elements
currently initializes red/blue/green colored hexagonal shapes
Ideas:
Different modes between which the initializer switches
The hope is that interesting behavioral patterns emerge
"""
class ElementInitiator:
    inlines = [ "BaseHex" ]
    basefrequency = 7

    #initialization of the ElementInitiator
    #handles importing of the modules and classes needed automatically
    #pump out tunnel element objects
    def __init__(self, tunnel, frequency = basefrequency):
        #upward reference to the tunnel manager
        self.tunnel = tunnel
        inlinemodule = importlib.import_module("inline")
        #the inlines list is populated with references to the class objects
        #of the inline classes named in the inlines list
        self.inlines = [ getattr(inlinemodule, inline) for inline in ElementInitiator.inlines ]
        #not used yet, but this is where the magic is planned
        self.wildness = 1
        #three basic RGB color values: a red, green and a blue
        self.colors = [[228,12,24], [12,228,24], [12,24,228]]
        self.lastcolor = 0
        #the frequency in ticks when new elements are produced
        self.frequency = frequency
        self.clock = frequency + 1

    #initializes a single TunnelElement
    def getelement(self):
        return TunnelElement(self.inlines[random.randrange(len(self.inlines))](), self.nextcolor(), self.tunnel)

    #adds a TunnelElement to the Tunnel managers element list
    def addelement(self, element):
        self.tunnel.elements.insert(0, element)

    #specifies one of the three base colors as next color
    #currently returns a FluctuatingFill object with that color
    #will return different kinds of Texture objects in the future
    #doesnt pick the same color twice in a row
    def nextcolor(self):
        index = random.randrange(len(self.colors) - 1)
        if index == self.lastcolor:
            index += 1
        self.lastcolor = index
        return FluctuatingFill(self.colors[index])

    #randomizes the initialized element
    #currently only randomizes rotation direction
    #this is where the magic with the wildness value is planned to play out
    def randomize_element(self, element):
        if bool(random.getrandbits(1)):
            element.inverse_turndirection()

    #act method, increases clock and spawns element if timing is met
    def act(self):
        self.clock += 1
        if self.clock > self.frequency:
            self.clock = 0
            element = self.getelement()
            self.randomize_element(element)
            self.addelement(element)

#alternative deprecated implementation of the tunnel class
#kept for redundancy
class CheapTunnel(Tunnel):
    def __init__(self, maincontroller):
        Tunnel.__init__(self, maincontroller)
        colors = [FluctuatingFill([228,12,24]), FluctuatingFill([12,228,24]), FluctuatingFill([12,24,228])]
        self.elements = [TunnelElement(Rectangle(100, 100), colors[i % 3], self) for i in range(9)]
        for i in range(len(self.elements)):
            self.elements[i].current_scale += i

    def act(self):
        for element in self.elements:
            element.act()
            if element.current_scale > 10:
                self.elements.remove(element)
                self.elements.insert(0, TunnelElement(Rectangle(100,100), element.texture, self))
                self.randomize_movement(self.elements[0])
                del element

    def randomize_movement(self, element):
        if bool(random.getrandbits(1)):
            element.rotation_angle = -element.rotation_angle


import urllib.parse
import tldextract
import requests
import json
import csv
import os
import re
import sys
import os
from flask import Flask, render_template, request
from bs4 import BeautifulSoup
import requests
import re
import validators
from urllib.parse import urlparse, urljoin
import urllib.parse
import whois21
import time
from datetime import datetime
from urllib.parse import urlencode
from selenium import webdriver
import signal

def parse_url(url):
    parsed_url = urlparse(url)
    hostname = parsed_url.scheme + "://" + parsed_url.netloc
    domain = parsed_url.netloc
    return hostname, domain, parsed_url.path

# RETURN CODES
# 0 stands for legitimate
# 1 stands for phishing

def get_url_length(url):
	return len(url)

def get_hostname_length(url):
	hostname, domain, path = parse_url(url)
	return len(hostname)

def having_ip_address(url):
    match = re.search(
        '(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.'
        '([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\/)|'  # IPv4
        '((0x[0-9a-fA-F]{1,2})\\.(0x[0-9a-fA-F]{1,2})\\.(0x[0-9a-fA-F]{1,2})\\.(0x[0-9a-fA-F]{1,2})\\/)|'  # IPv4 in hexadecimal
        '(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}|'
        '[0-9a-fA-F]{7}', url)  # Ipv6
    if match:
        return 1
    else:
        return 0

def count_hyphens(base_url):
	return base_url.count('-')

def count_and(base_url):
	return base_url.count('&')

def count_eq(base_url):
	return base_url.count('=')

def count_underscore(base_url):
	return base_url.count('_')

def count_slash(base_url):
	return base_url.count('/')

def https_token(url):
	if url[0:5] == 'https':
		return 0
	return 1

def port(url):
    if re.search("^[a-z][a-z0-9+\-.]*://([a-z0-9\-._~%!$&'()*+,;=]+@)?([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\]):([0-9]+)",url):
        return 1
    return 0

def get_number_of_hyperlinks(soup, url):

    hyperlinks = {'internal':0, 'external':0, 'null':0, 'total':0}

    base_url = urlparse(url).netloc

    for el in soup.find_all(href=True):

        if el['href'] in Null_format:
            hyperlinks['null']+=1
        else:
            full_url = urljoin(url, el.get('href'))
            parsed_url = urlparse(full_url)

            if parsed_url.netloc == base_url:
                hyperlinks['internal']+=1
            else:
                hyperlinks['external']+=1

        hyperlinks['total']+=1

    return hyperlinks

def get_null_hyperlinks_ratio(soup, domain):
    hyperlinks = get_number_of_hyperlinks(soup, domain)
    if hyperlinks['total']:
        return round(hyperlinks['null'] / hyperlinks['total'], 2)
    return None

def get_internal_hyperlinks_ratio(soup, domain):
    hyperlinks = get_number_of_hyperlinks(soup, domain)
    if hyperlinks['total']:
        return round(hyperlinks['internal'] / hyperlinks['total'], 2)
    return None

def get_external_hyperlinks_ratio(soup, domain):
    hyperlinks = get_number_of_hyperlinks(soup, domain)
    if hyperlinks['total']:
        return round(hyperlinks['external'] / hyperlinks['total'], 2)
    return None

def get_forms(soup, hostname, domain):
    Forms = {'internals': [], 'null': [], 'externals': []}
    # collect all forms
    for form in soup.find_all('form'):
        action = form.get('action')
        if action:
            if action.strip() in Null_format:
                Forms['null'].append(form)
            elif action.startswith('/'):
                Forms['internals'].append(form)
            else:
                Forms['externals'].append(form)
        else:
            Forms['null'].append(form)
    return Forms

def login_form(Forms):
    p = re.compile('([a-zA-Z0-9\_])+.php')
    if len(Forms['externals'])>0:
        return 'external login forms found'
    elif len(Forms['null'])>0:
        return 'null login forms found'
    for form in Forms['internals']+Forms['externals']:
        if p.match(str(form)) != None :
            return [form, "-s action is suspicious"]
    return None

def favicon_external_source(soup):
    try:
        favicon_link = soup.find('link', rel='icon') or soup.find('link', rel='shortcut icon')

        if favicon_link:
            favicon_url = favicon_link.get('href')
            parsed_url = urlparse(favicon_url)

            if parsed_url.netloc != '':
                # print("Favicon is loaded from an external source:", favicon_url)
                return favicon_url
            else:
                # print("Favicon is loaded from the same domain as the website.")
                return False
        else:
            # print("No favicon found on the website.")
            return None

    except requests.exceptions.RequestException as e:
        # print("An error occurred:", e)
        return None

def get_title(soup):
    return soup.title.string

def domain_in_title(domain, soup):
    # print("domain", domain.lower())
    # print("soup.title.string.lower()", soup.title.string.lower())
    if domain.lower() in get_title(soup).lower():
        return 1
    return None

def submitting_to_email(Form):
    for form in Form['internals'] + Form['externals']:
        if "mailto:" in form or "mail()" in form:
            return 1
        else:
            return None
    return None

def has_popup_window(url):
    try:
        # Initialize a Selenium webdriver
        driver = webdriver.Chrome()

        # Open the website
        driver.get(url)

        # Check if there are any open pop-up windows
        popup_handles = driver.window_handles
        if len(popup_handles) > 1:
            return(popup_handles)
        else:
            return(0)

        # Close the browser
        driver.quit()

    except Exception as e:
        return "An error occurred:", e

# ------------------------------- domain ---------

def whois_registered(domain):
    try:
        res = whois21.WHOIS(domain)
        return res
    except:
        return None

def domain_registration_length(domain):
    try:
        res = whois_registered(domain)
        # print("whois request:", res)
        expiration_date = res.expires_date
        # print("expiration_date: {}".format(expiration_date))
        today = time.strftime('%Y-%m-%d')
        today = datetime.strptime(today, '%Y-%m-%d')
        if expiration_date:
            if type(expiration_date) == list:
                expiration_date = min(expiration_date)
            return abs((expiration_date - today).days)
        else:
            return 0
    except:
        return None

def google_index(url):
    #time.sleep(.6)
    user_agent =  'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'
    headers = {'User-Agent' : user_agent}
    query = {'q': 'site:' + url}
    google = "https://www.google.com/search?" + urlencode(query)
    data = requests.get(google, headers=headers)
    data.encoding = 'ISO-8859-1'
    soup = BeautifulSoup(str(data.content), "html.parser")
    try:
        if 'Our systems have detected unusual traffic from your computer network.' in str(soup):
            return -1
        check = soup.find(id="rso").find("div").find("div").find("a")
        #print(check)
        if check and check['href']:
            return 0
        else:
            return 1

    except AttributeError:
        return 1

def result(url):
	content = getPageContent(url)
	if content != None:
		soup = BeautifulSoup(content, 'html.parser')
		hostname, domain, path = parse_url(url)
	print('\nlength URL:', get_url_length(url))
	print('\nlength hostname:', get_hostname_length(url))
	print('\nIP:', having_ip_address(url))
	print('\nnb of hyphens:', count_hyphens(url))
	print('\nnb of and:', count_and(url))
	print('\nnb of eq:', count_eq(url))
	print('\nnb of underscore:', count_underscore(url))
	print('\nnb of slash:', count_slash(url))
	print('\nhttps token:', https_token(url))
	print('\nport:', port(url))
	print('\nbrand: CHECK')
	print('\nnb of hyperlinks:', get_number_of_hyperlinks(soup, url)['total'])
	print('\nratio of internal hyperlinks:', get_internal_hyperlinks_ratio(soup, domain))
	print('\nratio of external hyperlinks:', get_external_hyperlinks_ratio(soup, domain))
	print('\nratio of null hyperlinks:', get_null_hyperlinks_ratio(soup, domain))
	print('\nlogin form:', login_form(get_forms(soup, hostname, domain)))
	print('\nexternal favicon:', favicon_external_source(soup))
	print('\nsubmitting to email:', submitting_to_email(get_forms(soup, hostname, domain)))
	print('\niframe:', 1 if soup.findAll('iframe') else 0)
	print('\npop up:', has_popup_window(url))
	print('\ndomain_in_title:', domain_in_title(domain, soup))
	print('\nwhois_registered:', whois_registered(domain))
	print('\ndomain_registration_length:', domain_registration_length(domain))
	print('\ngoogle_index:', google_index(url))
	print('-------------------------------------------\n')

while True:
	url = input("Please enter url: ")
	result(url)


import logging
import os
import sys

from helpers.dynamodb_helper import DynamoDbHelper
# pylint: disable=import-error
from helpers.response import ResponseHelper
from helpers.utils import convert_string_to_bool

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    logger.info(event)
    status_code = 200
    message = "Record(s) found"
    records = None

    try:
        user_id = event["pathParameters"]["user_id"]
        user_context = event["requestContext"]["authorizer"]

        is_allow_list = True
        if not convert_string_to_bool(user_context["is_admin"]) is True:
            if not user_id == user_context["id"]:
                is_allow_list = False

        if is_allow_list:
            records = get_records(user_id)
        else:
            status_code = 403
            message = "You are not authorized to perform this action"

    except Exception as exception:
        logger.error("Exception: {}".format(exception), exc_info=sys.exc_info())
        status_code = 500
        message = "Process has encountered an internal server exception."

    response = {
        "message": message
    }

    if not records is None and not len(records) == 0:
        response['Items'] = records
    elif not status_code == 500 and not status_code == 403:
        response["message"] = "Record(s) not found"

    return ResponseHelper.format_response(status_code, response)

def get_records(user_id):
    params = {
        "KeyConditionExpression": "#user_id = :user_id and \
                                 #created_time_epoch > :intZero",
        "ExpressionAttributeNames": {
            '#user_id': 'user_id',
            "#created_time_epoch": 'created_time_epoch'
        },
        "ExpressionAttributeValues": {
            ":user_id": user_id,
            ':intZero': 0,
        },
        "ScanIndexForward": False,
        "IndexName": "user_id_created_time_epoch_index"
    }
    return DynamoDbHelper.query(os.environ['APPLICATION_TRACKER_TABLE_NAME'], params)


import collections
import sys

from misc.codegen.generators import dbschemegen
from misc.codegen.lib import dbscheme
from misc.codegen.test.utils import *

InputExpectedPair = collections.namedtuple("InputExpectedPair", ("input", "expected"))

@pytest.fixture(params=[
    InputExpectedPair(None, None),
    InputExpectedPair("foodir", pathlib.Path("foodir")),
])
def dir_param(request):
    return request.param

@pytest.fixture
def generate(opts, input, renderer):
    def func(classes, null=None):
        input.classes = {cls.name: cls for cls in classes}
        input.null = null
        (out, data), = run_generation(dbschemegen.generate, opts, renderer).items()
        assert out is opts.dbscheme
        return data

    return func

def test_empty(generate):
    assert generate([]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[],
    )

def test_includes(input, opts, generate):
    includes = ["foo", "bar"]
    input.includes = includes
    for i in includes:
        write(opts.schema.parent / i, i + " data")

    assert generate([]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[
            dbscheme.SchemeInclude(
                src=pathlib.Path(i),
                data=i + " data",
            ) for i in includes
        ],
        declarations=[],
    )

def test_empty_final_class(generate, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                ],
                dir=dir_param.expected,
            )
        ],
    )

def test_final_class_with_single_scalar_field(generate, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input, properties=[
            schema.SingleProperty("foo", "bar"),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                    dbscheme.Column('foo', 'bar'),
                ], dir=dir_param.expected,
            )
        ],
    )

def test_final_class_with_single_class_field(generate, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input, properties=[
            schema.SingleProperty("foo", "Bar"),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                    dbscheme.Column('foo', '@bar'),
                ], dir=dir_param.expected,
            )
        ],
    )

def test_final_class_with_optional_field(generate, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input, properties=[
            schema.OptionalProperty("foo", "bar"),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_foos",
                keyset=dbscheme.KeySet(["id"]),
                columns=[
                    dbscheme.Column('id', '@object'),
                    dbscheme.Column('foo', 'bar'),
                ], dir=dir_param.expected,
            ),
        ],
    )

@pytest.mark.parametrize("property_cls", [schema.RepeatedProperty, schema.RepeatedOptionalProperty])
def test_final_class_with_repeated_field(generate, property_cls, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input, properties=[
            property_cls("foo", "bar"),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_foos",
                keyset=dbscheme.KeySet(["id", "index"]),
                columns=[
                    dbscheme.Column('id', '@object'),
                    dbscheme.Column('index', 'int'),
                    dbscheme.Column('foo', 'bar'),
                ], dir=dir_param.expected,
            ),
        ],
    )

def test_final_class_with_repeated_unordered_field(generate, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input, properties=[
            schema.RepeatedUnorderedProperty("foo", "bar"),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_foos",
                columns=[
                    dbscheme.Column('id', '@object'),
                    dbscheme.Column('foo', 'bar'),
                ], dir=dir_param.expected,
            ),
        ],
    )

def test_final_class_with_predicate_field(generate, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input, properties=[
            schema.PredicateProperty("foo"),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_foo",
                keyset=dbscheme.KeySet(["id"]),
                columns=[
                    dbscheme.Column('id', '@object'),
                ], dir=dir_param.expected,
            ),
        ],
    )

def test_final_class_with_more_fields(generate, dir_param):
    assert generate([
        schema.Class("Object", group=dir_param.input, properties=[
            schema.SingleProperty("one", "x"),
            schema.SingleProperty("two", "y"),
            schema.OptionalProperty("three", "z"),
            schema.RepeatedProperty("four", "u"),
            schema.RepeatedOptionalProperty("five", "v"),
            schema.PredicateProperty("six"),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="objects",
                columns=[
                    dbscheme.Column('id', '@object', binding=True),
                    dbscheme.Column('one', 'x'),
                    dbscheme.Column('two', 'y'),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_threes",
                keyset=dbscheme.KeySet(["id"]),
                columns=[
                    dbscheme.Column('id', '@object'),
                    dbscheme.Column('three', 'z'),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_fours",
                keyset=dbscheme.KeySet(["id", "index"]),
                columns=[
                    dbscheme.Column('id', '@object'),
                    dbscheme.Column('index', 'int'),
                    dbscheme.Column('four', 'u'),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_fives",
                keyset=dbscheme.KeySet(["id", "index"]),
                columns=[
                    dbscheme.Column('id', '@object'),
                    dbscheme.Column('index', 'int'),
                    dbscheme.Column('five', 'v'),
                ], dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="object_six",
                keyset=dbscheme.KeySet(["id"]),
                columns=[
                    dbscheme.Column('id', '@object'),
                ], dir=dir_param.expected,
            ),
        ],
    )

def test_empty_class_with_derived(generate):
    assert generate([
        schema.Class(name="Base", derived={"Left", "Right"}),
        schema.Class(name="Left", bases=["Base"]),
        schema.Class(name="Right", bases=["Base"]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Union(
                lhs="@base",
                rhs=["@left", "@right"],
            ),
            dbscheme.Table(
                name="lefts",
                columns=[dbscheme.Column("id", "@left", binding=True)],
            ),
            dbscheme.Table(
                name="rights",
                columns=[dbscheme.Column("id", "@right", binding=True)],
            ),
        ],
    )

def test_class_with_derived_and_single_property(generate, dir_param):
    assert generate([
        schema.Class(
            name="Base",
            derived={"Left", "Right"},
            group=dir_param.input,
            properties=[
                schema.SingleProperty("single", "Prop"),
            ]),
        schema.Class(name="Left", bases=["Base"]),
        schema.Class(name="Right", bases=["Base"]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Union(
                lhs="@base",
                rhs=["@left", "@right"],
            ),
            dbscheme.Table(
                name="bases",
                keyset=dbscheme.KeySet(["id"]),
                columns=[
                    dbscheme.Column('id', '@base'),
                    dbscheme.Column('single', '@prop'),
                ],
                dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="lefts",
                columns=[dbscheme.Column("id", "@left", binding=True)],
            ),
            dbscheme.Table(
                name="rights",
                columns=[dbscheme.Column("id", "@right", binding=True)],
            ),
        ],
    )

def test_class_with_derived_and_optional_property(generate, dir_param):
    assert generate([
        schema.Class(
            name="Base",
            derived={"Left", "Right"},
            group=dir_param.input,
            properties=[
                schema.OptionalProperty("opt", "Prop"),
            ]),
        schema.Class(name="Left", bases=["Base"]),
        schema.Class(name="Right", bases=["Base"]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Union(
                lhs="@base",
                rhs=["@left", "@right"],
            ),
            dbscheme.Table(
                name="base_opts",
                keyset=dbscheme.KeySet(["id"]),
                columns=[
                    dbscheme.Column('id', '@base'),
                    dbscheme.Column('opt', '@prop'),
                ],
                dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="lefts",
                columns=[dbscheme.Column("id", "@left", binding=True)],
            ),
            dbscheme.Table(
                name="rights",
                columns=[dbscheme.Column("id", "@right", binding=True)],
            ),
        ],
    )

def test_class_with_derived_and_repeated_property(generate, dir_param):
    assert generate([
        schema.Class(
            name="Base",
            group=dir_param.input,
            derived={"Left", "Right"},
            properties=[
                schema.RepeatedProperty("rep", "Prop"),
            ]),
        schema.Class(name="Left", bases=["Base"]),
        schema.Class(name="Right", bases=["Base"]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Union(
                lhs="@base",
                rhs=["@left", "@right"],
            ),
            dbscheme.Table(
                name="base_reps",
                keyset=dbscheme.KeySet(["id", "index"]),
                columns=[
                    dbscheme.Column('id', '@base'),
                    dbscheme.Column('index', 'int'),
                    dbscheme.Column('rep', '@prop'),
                ],
                dir=dir_param.expected,
            ),
            dbscheme.Table(
                name="lefts",
                columns=[dbscheme.Column("id", "@left", binding=True)],
            ),
            dbscheme.Table(
                name="rights",
                columns=[dbscheme.Column("id", "@right", binding=True)],
            ),
        ],
    )

def test_null_class(generate):
    assert generate([
        schema.Class(
            name="Base",
            derived={"W", "X", "Y", "Z", "Null"},
        ),
        schema.Class(
            name="W",
            bases=["Base"],
            properties=[
                schema.SingleProperty("w", "W"),
                schema.SingleProperty("x", "X"),
                schema.OptionalProperty("y", "Y"),
                schema.RepeatedProperty("z", "Z"),
            ]
        ),
        schema.Class(
            name="X",
            bases=["Base"],
        ),
        schema.Class(
            name="Y",
            bases=["Base"],
        ),
        schema.Class(
            name="Z",
            bases=["Base"],
        ),
        schema.Class(
            name="Null",
            bases=["Base"],
        ),
    ], null="Null") == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Union(
                lhs="@base",
                rhs=["@null", "@w", "@x", "@y", "@z"],
            ),
            dbscheme.Table(
                name="ws",
                columns=[
                    dbscheme.Column('id', '@w', binding=True),
                    dbscheme.Column('w', '@w_or_none'),
                    dbscheme.Column('x', '@x_or_none'),
                ],
            ),
            dbscheme.Table(
                name="w_ies",
                keyset=dbscheme.KeySet(["id"]),
                columns=[
                    dbscheme.Column('id', '@w'),
                    dbscheme.Column('y', '@y_or_none'),
                ],
            ),
            dbscheme.Table(
                name="w_zs",
                keyset=dbscheme.KeySet(["id", "index"]),
                columns=[
                    dbscheme.Column('id', '@w'),
                    dbscheme.Column('index', 'int'),
                    dbscheme.Column('z', '@z_or_none'),
                ],
            ),
            dbscheme.Table(
                name="xes",
                columns=[
                    dbscheme.Column('id', '@x', binding=True),
                ],
            ),
            dbscheme.Table(
                name="ys",
                columns=[
                    dbscheme.Column('id', '@y', binding=True),
                ],
            ),
            dbscheme.Table(
                name="zs",
                columns=[
                    dbscheme.Column('id', '@z', binding=True),
                ],
            ),
            dbscheme.Table(
                name="nulls",
                columns=[
                    dbscheme.Column('id', '@null', binding=True),
                ],
            ),
            dbscheme.Union(
                lhs="@w_or_none",
                rhs=["@w", "@null"],
            ),
            dbscheme.Union(
                lhs="@x_or_none",
                rhs=["@x", "@null"],
            ),
            dbscheme.Union(
                lhs="@y_or_none",
                rhs=["@y", "@null"],
            ),
            dbscheme.Union(
                lhs="@z_or_none",
                rhs=["@z", "@null"],
            ),
        ],
    )

def test_synth_classes_ignored(generate):
    assert generate([
        schema.Class(name="A", synth=schema.SynthInfo()),
        schema.Class(name="B", synth=schema.SynthInfo(from_class="A")),
        schema.Class(name="C", synth=schema.SynthInfo(on_arguments={"x": "A"})),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[],
    )

def test_synth_derived_classes_ignored(generate):
    assert generate([
        schema.Class(name="A", derived={"B", "C"}),
        schema.Class(name="B", bases=["A"], synth=schema.SynthInfo()),
        schema.Class(name="C", bases=["A"]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Union("@a", ["@c"]),
            dbscheme.Table(
                name="cs",
                columns=[
                    dbscheme.Column("id", "@c", binding=True),
                ],
            )
        ],
    )

def test_synth_properties_ignored(generate):
    assert generate([
        schema.Class(name="A", properties=[
            schema.SingleProperty("x", "a"),
            schema.SingleProperty("y", "b", synth=True),
            schema.SingleProperty("z", "c"),
            schema.OptionalProperty("foo", "bar", synth=True),
            schema.RepeatedProperty("baz", "bazz", synth=True),
            schema.RepeatedOptionalProperty("bazzz", "bazzzz", synth=True),
            schema.RepeatedUnorderedProperty("bazzzzz", "bazzzzzz", synth=True),
        ]),
    ]) == dbscheme.Scheme(
        src=schema_file.name,
        includes=[],
        declarations=[
            dbscheme.Table(
                name="as",
                columns=[
                    dbscheme.Column("id", "@a", binding=True),
                    dbscheme.Column("x", "a"),
                    dbscheme.Column("z", "c"),
                ],
            )
        ],
    )

if __name__ == '__main__':
    sys.exit(pytest.main([__file__] + sys.argv[1:]))


import pandas as pd
import numpy as np
import datetime
import matplotlib.pyplot as plt

dfs = pd.read_csv("/Users/anita/Documents/SpringSemester/SoftwareEngineering/dataprediction/stations.csv", delimiter=';')

#print(dfs.tail())

# assume you have a DataFrame object called "stations_df" with the data already loaded
station_names = dfs['nameStation'].tolist()

# print the list of station names
#print(len(station_names))

dfa = pd.read_csv("/Users/anita/Documents/SpringSemester/SoftwareEngineering/dataprediction/predictionDataSE.csv", delimiter=';')

print(dfa.head())

# Update the nameStation column with the station names cyclically
def nameupdate():
    num_stations = len(station_names)
    num_rows = len(dfa)
    indices = np.arange(num_rows) % num_stations
    dfa["nameStation"] = [station_names[i] for i in indices]

print(dfa.head())

# Save the updated DataFrame to a new CSV file
dfa.to_csv("/Users/anita/Documents/SpringSemester/SoftwareEngineering/dataprediction/updatedpredictionDataSE.csv", index=False, sep=";")
dfu = pd.read_csv("/Users/anita/Documents/SpringSemester/SoftwareEngineering/dataprediction/updatedpredictionDataSE.csv", delimiter=';', parse_dates=['lastUpdate'])

print(dfu.tail())

# extract the hour from the lastUpdate column and create a new column
dfu['lastUpdate'] = pd.to_datetime(dfu['lastUpdate'], unit='ms')
dfu['hour'] = dfu['lastUpdate'].dt.hour

# plot the hour column on the x-axis and availableBikes column on the y-axis
#plt.plot(dfu['hour'], dfu['availableBikes'])
#plt.xlabel('Hour')
#plt.ylabel('Available Bikes')
#plt.show()

# iterate over the station names and create a separate plot for each station
for i in station_names:
    # filter the DataFrame to only include data for this station
    station_df = dfu[dfu['nameStation'] == i]

    # plot the hour column on the x-axis and availableBikes column on the y-axis
    plt.plot(station_df['hour'], station_df['availableBikes'])
    plt.xlabel('Hour')
    plt.ylabel('Available Bikes')
    plt.title(i)
    plt.show()