from logging import getLogger
from gevent import spawn, sleep
from fibpro.config import (PROD_HOSTNAME, PROD_PORT, DEV_HOSTNAME, SERVICE_UPDATE_INTERVAL,
    DEFAULT_SERVICE_DIR_ENDPOINT, DEFAULT_ENVIRONMENT)
from fibpro.rpc import GenericClient, Server, get_request_meta, get_server_meta, set_server_meta
from fibpro.util import load_config, dict_map_string, dict_set, dict_get

class ServiceDirBase(object):
    NAME = "servicedir"
    LOG_RPC = True
    # servicedir must log to stderr instead of logsink
    log = getLogger('gunicorn.error')

class ServiceDirServer(ServiceDirBase, Server):

    def server_init(self):
        self.service_endpoints = {}

    def register_server(self):
        pass

    def get_environments(self):
        return self.service_endpoints.keys()

    def get_services(self, environment=DEFAULT_ENVIRONMENT):
        return dict_get(self.service_endpoints, ['environment'], {}).keys()

    def get_endpoint(self, environment=DEFAULT_ENVIRONMENT, service=None):
        return dict_get(self.service_endpoints, [environment, service], None)

    def get_all_endpoints(self, environment=None):
        if environment is None:
            return self.service_endpoints
        return dict_get(self.service_endpoints, [environment], {})

    def set_all_endpoints(self, endpoints=None, environment=None):
        if environment is None:
            self.service_endpoints = endpoints
        else:
            dict_set(self.service_endpoints, [environment], endpoints)

    def set_endpoint(self, environment=DEFAULT_ENVIRONMENT, service=None, endpoint_parts=None):
        endpoint = "{scheme}://{host}:{port}/{path}".format(
            scheme=endpoint_parts.get('scheme', 'http'),
            host=endpoint_parts.get('host', get_request_meta().get('environ')['REMOTE_ADDR']),
            port=endpoint_parts['port'],
            path=endpoint_parts['path'])
        self.log.info("Registering endpoint %s:%s = %s" % (
            environment, service, endpoint))
        dict_set(self.service_endpoints, [environment, service], endpoint)
        return endpoint

class ServiceDirClient(ServiceDirBase, GenericClient):

    def __init__(
            self,
            service_dir_endpoint=None):
        self.name = self.NAME
        self.service_dir_endpoint = service_dir_endpoint or DEFAULT_SERVICE_DIR_ENDPOINT
        self.service_dir_client = self
        self.service_endpoints = {}
        self._init_timer()

    def _init_timer(self):
        def poll_endpoints(server_meta):
            # set server meta for this thread
            set_server_meta(server_meta)
            while True:
                self.service_endpoints = self.get_all_endpoints(
                    environment=dict_get(
                        get_server_meta(),
                        ["environment"],
                        DEFAULT_ENVIRONMENT))
                sleep(SERVICE_UPDATE_INTERVAL)
        spawn(poll_endpoints, get_server_meta())

    def get_endpoint(self, environment, service):
        if service == "servicedir":
            return self.service_dir_endpoint
        if self.service_endpoints and \
                service in self.service_endpoints.get(environment, {}):
            endpoint = self.service_endpoints[environment][service]
            if endpoint is not None:
                return endpoint
        # fall back to making RPC request
        self.log.info("No cached endpoint for %s; polling servicedir" % service)
        return self.call('get_endpoint', {
            'environment': environment,
            'service': service})

    def _get_current_environment(self):
        # first, try to get environment from request_meta,
        # then from server_meta, then use default
        return get_request_meta().get(
            'environment',
            get_server_meta().get('environment',
                DEFAULT_ENVIRONMENT))

    def _get_endpoint_with_overrides(self, environment, service):
        is_custom_endpoint = False
        endpoint = self.get_endpoint(environment, service)
        custom_endpoint = get_request_meta().get('custom_endpoints', {}).get(service, None)
        if custom_endpoint:
            is_custom_endpoint = True
            # custom_endpoint could be an environment name
            # or an endpoint url
            # TEMPORARILY DISABLE
            # if custom_endpoint in self.get_environments():
            #    endpoint = self.endpoints[custom_endpoint].get(service)
            #else:
            endpoint = custom_endpoint
        return endpoint, is_custom_endpoint

    def get_effective_endpoint(self, service):
        """ returns effective endpoint for service with overrides. """
        environment = self._get_current_environment()
        return self._get_endpoint_with_overrides(environment, service)


import uuid
from datetime import date, datetime
from secrets import token_urlsafe

from invenio_db import db
from sqlalchemy_utils import UUIDType

from .permissions import AccessRequestTokenNeed

class AccessRequestToken(db.Model):
    """Token for accessing guest-based access requests.

    These tokens are used in the guest-based access request workflow to
    signify that somebody has requested access to a record via e-mail,
    but hasn't verified their e-mail yet.
    Once the e-mail address is verified to create the access request, this
    the corresponding ``AccessRequestToken`` will be deleted, and a new
    guest access request will be created with some of the token's information.
    """

    __tablename__ = "rdm_records_access_request_tokens"

    id = db.Column(UUIDType, primary_key=True, default=uuid.uuid4)
    """Access request token ID."""

    token = db.Column(db.String(512), nullable=False)
    """Randomly generated token which will grant access to the request."""

    created = db.Column(
        db.DateTime, nullable=False, default=datetime.utcnow, index=True
    )
    """Creation timestamp."""

    expires_at = db.Column(db.DateTime, nullable=False)
    """Expiration date."""

    email = db.Column(db.String(255), nullable=False)
    """E-mail address for which the access request was made."""

    full_name = db.Column(db.String(255), nullable=False, default="")
    """Full name of the requester."""

    message = db.Column(db.Text, nullable=False, default="")
    """Message associated with the request, explaining why access is needed."""

    record_pid = db.Column(db.String(255), nullable=False)
    """PID value of the record for which the request will be created."""

    consent_to_share_personal_data = db.Column(db.String(255), nullable=False)

    def delete(self):
        """Delete this secret link."""
        db.session.delete(self)

    def to_dict(self):
        """Dump the access request token to a dictionary."""
        return {
            "id": str(self.id),
            "token": str(self.token),
            "created_at": self.created,
            "expires_at": self.expires_at,
            "email": self.email,
            "full_name": self.full_name,
            "message": self.message,
            "consent_to_share_personal_data": self.consent_to_share_personal_data,
            "record_pid": self.record_pid,
        }

    @property
    def need(self):
        """Get the needs provided by the link."""
        return AccessRequestTokenNeed(str(self.id))

    @property
    def is_expired(self):
        """Determine if link is expired."""
        return datetime.utcnow() > self.expires_at

    @classmethod
    def get_by_token(cls, token):
        """Get the AccessRequestToken referenced by the specified token."""
        return cls.query.filter_by(token=token).one_or_none()

    @classmethod
    def create(
        cls,
        email,
        full_name,
        message,
        record_pid,
        shelf_life=None,
        expires_at=None,
        consent=False,
    ):
        """Create a new request access token.

        The ``expires_at`` value will take precedence over the ``shelf_life``.

        :param email: The email address for which the request is made.
        :param full_name: The full name of the requester.
        :param message: The message attached to the request.
        :param record_pid: The PID value for the record that is subject to the request.
        :param shelf_life: The ``timedelta`` for how long the token will live for.
        :param expires_at: The expiration date of the access request token.
        """
        if shelf_life and not expires_at:
            expires_at = datetime.utcnow() + shelf_life

        is_date = isinstance(expires_at, date)
        is_datetime = isinstance(expires_at, datetime)
        if is_date and not is_datetime:
            expires_at = datetime.combine(expires_at, datetime.min.time())

        access_request_token = cls(
            email=email,
            full_name=full_name,
            message=message,
            record_pid=record_pid,
            expires_at=expires_at,
            token=token_urlsafe(),
            consent_to_share_personal_data=consent,
        )
        db.session.add(access_request_token)

        return access_request_token


import os
import sys
import json
import time
import logging
import data_helper
import numpy as np
import tensorflow as tf
from original import TextCNN
from tensorflow.contrib import learn
from sklearn.model_selection import train_test_split

logging.getLogger().setLevel(logging.INFO)

def train_cnn(dataset_name):
    """Step 0: load sentences, labels, and training parameters"""
    dataset = '../dataset/'+dataset_name+'_csv/train.csv'
    testset = '../dataset/'+dataset_name+'_csv/test.csv'
    parameter_file = "./parameters.json"
    params = json.loads(open(parameter_file).read())
    x_raw, y_raw, df, labels = data_helper.load_data_and_labels(dataset,dataset_name,True)
    x_test, y_test, df, labels = data_helper.load_data_and_labels(testset,dataset_name,False)

    """Step 1: pad each sentence to the same length and map each word to an id"""
    max_document_length = max([len(x.split(' ')) for x in x_raw])
    logging.info('The maximum length of all sentences: {}'.format(max_document_length))
    vocab_processor = learn.preprocessing.VocabularyProcessor(max_document_length)
    x = np.array(list(vocab_processor.fit_transform(x_raw)))
    y = np.array(y_raw)
    x_test = np.array(list(vocab_processor.fit_transform(x_test)))
    y_test = np.array(y_test)

    """Step 3: shuffle the train set and split the train set into train and dev sets"""
    shuffle_indices = np.random.permutation(np.arange(len(y)))
    x_shuffled = x[shuffle_indices]
    y_shuffled = y[shuffle_indices]
    x_train, x_dev, y_train, y_dev = train_test_split(x_shuffled, y_shuffled, test_size=0.1)

    """Step 4: save the labels into labels.json since predict.py needs it"""
    with open('./labels.json', 'w') as outfile:
        json.dump(labels, outfile, indent=4)

    logging.info('x_train: {}, x_dev: {}, x_test: {}'.format(len(x_train), len(x_dev), len(x_test)))
    logging.info('y_train: {}, y_dev: {}, y_test: {}'.format(len(y_train), len(y_dev), len(y_test)))

    """Step 5: build a graph and cnn object"""
    graph = tf.Graph()
    with graph.as_default():
        session_conf = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False)
        sess = tf.Session(config=session_conf)
        with sess.as_default():
            cnn = TextCNN(
                sequence_length=x_train.shape[1],
                num_classes=y_train.shape[1],
                vocab_size=len(vocab_processor.vocabulary_),
                embedding_size=params['embedding_dim'],
                filter_sizes=list(map(int, params['filter_sizes'].split(","))),
                num_filters=params['num_filters'])

            global_step = tf.Variable(0, name="global_step", trainable=False)
            epsilon=params['epsilon']
            num_batches_per_epoch = int((len(x_train)-1)/params['batch_size']) + 1
            learning_rate = tf.train.exponential_decay(params['learning_rate'], global_step,num_batches_per_epoch, 0.95, staircase=True)

            optimizer = tf.train.AdamOptimizer(learning_rate,epsilon)
            update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

            gradients, variables = zip(*optimizer.compute_gradients(cnn.loss))
            gradients, _ = tf.clip_by_global_norm(gradients, 7.0)

            with tf.control_dependencies(update_ops):
                train_op = optimizer.apply_gradients(zip(gradients, variables), global_step=global_step)

            timestamp = str(int(time.time()))

            out_dir = os.path.abspath(os.path.join(os.path.curdir, "model_" + timestamp))

            checkpoint_dir = os.path.abspath(os.path.join(out_dir, "checkpoints"))
            checkpoint_prefix = os.path.join(checkpoint_dir, "model")
            if not os.path.exists(checkpoint_dir):
                os.makedirs(checkpoint_dir)
            saver = tf.train.Saver(tf.global_variables())

            # One training step: train the model with one batch
            def train_step(x_batch, y_batch):
                feed_dict = {
                    cnn.input_x: x_batch,
                    cnn.input_y: y_batch,
                    cnn.is_training: True,
                    cnn.dropout_keep_prob: params['dropout_keep_prob']}
                _, step, loss, acc = sess.run([train_op, global_step, cnn.loss, cnn.accuracy], feed_dict)
                return acc,loss

            # One evaluation step: evaluate the model with one batch
            def dev_step(x_batch, y_batch):
                feed_dict = {cnn.input_x: x_batch,
                             cnn.input_y: y_batch,
                             cnn.is_training: False,
                             cnn.dropout_keep_prob: 1.0}
                step, loss, acc, num_correct = sess.run([global_step, cnn.loss, cnn.accuracy, cnn.num_correct],
                                                        feed_dict)
                return num_correct

            # Save the word_to_id map since predict.py needs it
            vocab_processor.save(os.path.join(out_dir, "vocab.pickle"))
            sess.run(tf.global_variables_initializer())

            # Training starts here
            train_batches = data_helper.batch_iter(list(zip(x_train, y_train)), params['batch_size'],
                                                   params['num_epochs'])
            best_accuracy, best_at_step = 0, 0

            """Step 6: train the cnn model with x_train and y_train (batch by batch)"""
            for train_batch in train_batches:
                x_train_batch, y_train_batch = zip(*train_batch)
                train_acc,  train_loss = train_step(x_train_batch, y_train_batch)
                current_step = tf.train.global_step(sess, global_step)
                """Step 6.1: evaluate the model with x_dev and y_dev (batch by batch)"""
                if current_step % params['evaluate_every'] == 0:
                    logging.critical('step: {} accuracy: {} cnn_loss: {} '.format(current_step, train_acc, train_loss))
                    dev_batches = data_helper.batch_iter(list(zip(x_dev, y_dev)), params['batch_size'], 1)
                    total_dev_correct = 0
                    for dev_batch in dev_batches:
                        x_dev_batch, y_dev_batch = zip(*dev_batch)
                        num_dev_correct = dev_step(x_dev_batch, y_dev_batch)
                        total_dev_correct += num_dev_correct

                    dev_accuracy = float(total_dev_correct) / len(y_dev)
                    logging.critical('Accuracy on dev set: {}'.format(dev_accuracy))

                    """Step 6.2: save the model if it is the best based on accuracy on dev set"""
                    if dev_accuracy >= best_accuracy:
                        best_accuracy, best_at_step = dev_accuracy, current_step
                        path = saver.save(sess, checkpoint_prefix, global_step=current_step)
                        logging.critical('Saved model at {} at step {}'.format(path, best_at_step))
                        logging.critical('Best accuracy is {} at step {}'.format(best_accuracy, best_at_step))

            """Step 7: predict x_test (batch by batch)"""
            test_batches = data_helper.batch_iter(list(zip(x_test, y_test)), params['batch_size'], 1)
            total_test_correct = 0
            start=time.time()
            for test_batch in test_batches:
                x_test_batch, y_test_batch = zip(*test_batch)
                num_test_correct = dev_step(x_test_batch, y_test_batch)
                total_test_correct += num_test_correct
            #path = saver.save(sess, checkpoint_prefix)
            logging.critical("\nExecution time for testing = {0:.6f}".format(time.time() - start))
            test_accuracy = float(total_test_correct) / len(y_test)
            logging.critical('Accuracy on test set is {} based on the best model {}'.format(test_accuracy, path))
            logging.critical('The training is complete')

if __name__ == '__main__':
    # python3 train.py ./data/consumer_complaints.csv.zip ./parameters.json
    train_cnn(sys.argv[1])


"""Sphinx configuration."""

from __future__ import annotations

from datetime import datetime
from datetime import timezone as tz
from importlib.metadata import metadata
from pathlib import PurePosixPath
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from sphinx.application import Sphinx

extensions = [
    "sphinx.ext.intersphinx",
    "sphinx.ext.napoleon",
    "sphinx_autodoc_typehints",  # needs to be after napoleon
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
    "scanpydoc",
    "sphinx.ext.linkcode",  # needs to be after scanpydoc
]

intersphinx_mapping = dict(
    python=("https://docs.python.org/3", None),
    jinja=("https://jinja.palletsprojects.com/en/2.10.x/", None),
    sphinx=("https://www.sphinx-doc.org/en/master/", None),
    sphinx_book_theme=("https://sphinx-book-theme.readthedocs.io/en/stable/", None),
    # examples
    numpy=("https://numpy.org/doc/stable/", None),
    anndata=("https://anndata.readthedocs.io/en/latest/", None),
    pandas=("https://pandas.pydata.org/pandas-docs/stable/", None),
    scipy=("https://docs.scipy.org/doc/scipy/", None),
)

# general information
meta = metadata("scanpydoc")
project = meta["name"]
author = meta["author-email"].split(" <")[0]
copyright = f"{datetime.now(tz=tz.utc):%Y}, {author}."  # noqa: A001
version = release = meta["version"]

master_doc = "index"
templates_path = ["_templates"]

# Generate .rst stubs for modules using autosummary
autosummary_generate = True
autosummary_ignore_module_all = False
# Don’t add module paths to documented functions’ names
add_module_names = False

html_theme = "scanpydoc"
html_theme_options = dict(
    repository_url="https://github.com/theislab/scanpydoc",
    repository_branch="main",
    use_repository_button=True,
)

rtd_links_prefix = PurePosixPath("src")

def setup(app: Sphinx) -> None:
    """Set up custom Sphinx extension."""
    app.add_object_type(
        "confval",
        "confval",
        objname="configuration value",
        indextemplate="pair: %s; configuration value",
    )


"""Sample a set of points in a low-dimensional embedding, according to some
distribution. Points must fit in memory.

"""

import numpy as np
import scipy
import logging
from sklearn.cluster import (SpectralClustering,
                             KMeans,
                             AgglomerativeClustering,
                             DBSCAN)
from sklearn.mixture import GaussianMixture

logger = logging.getLogger('latens')

class Sampler:
  def __init__(self, sample_size=None):
    """A callable that returns an integer array indexing the points given to it.

    :param sample_size: number of examples that this sampler takes. Not used by
    every Sampler, but included for consistency. Can be a fraction, in which
    case len(points) * sample_size are taken from points. Can be None, in
    which case all points are taken.
    :returns:
    :rtype:

    """
    self.sample_size = sample_size

  def get_sample_size(self, N):
    """Get the number of examples to take from a set of points.

    :param N: number of original points
    :returns: sample_size
    :rtype:

    """
    if self.sample_size is None:
      return int(N)
    elif self.sample_size >= 1:
      return int(self.sample_size)
    elif 0 <= self.sample_size < 1:
      return int(np.floor(self.sample_size * N))
    else:
      raise ValueError(f'unrecognized sample_size: {self.sample_size}')

  def __call__(self, *args, **kwargs):
    """Wrapper around self.sample()"""
    return self.sample(*args, **kwargs)

  def sample(self, points):
    """Select some subset of points.

    Returns an array indexing into points. Specifies number of times to repeat
    each example (could be interpreted as a boolean array).

    :param points: points to sample
    :returns: integer or boolean array for indexing points, i.e. a "sampling"
    :rtype: 1-D array

    """
    return np.ones(points.shape[0], dtype=np.int64)

class IdentitySampler(Sampler):
  def sample(self, points):
    return points

class RandomSampler(Sampler):
  def sample(self, points):
    N = points.shape[0]
    n = self.get_sample_size(N)
    perm = np.random.permutation(N)
    sampling = np.zeros(N, dtype=np.int64)
    sampling[perm[:n]] = 1
    return sampling

class MaximizingSampler(Sampler):
  """Provided metrics or encodings, selects the indices which minimize some
  function on those encodings. In the simplest case, provided MAE, return
  examples which maximize the MAE (that is, those on which the autoencoder did
  the worst).

  Subclasses can override process.

  """

  def process(self, points):
    """Given points, compute the row-wise value wished to maximized on."""
    return np.max(points, axis=1)

  def sample(self, points):
    N = points.shape[0]
    n = self.get_sample_size(points.shape[0])
    logger.debug(f"points: {points.shape}, {points}")
    points = points.reshape(N, -1)
    values = self.process(points)
    logger.debug(f"values: {values.shape}")
    indices = np.argsort(values)
    sampling = np.zeros(N, dtype=np.int64)
    sampling[indices[-n:]] = 1
    logger.debug(f"sampling: {sampling}")
    return sampling

class SpatialSampler(Sampler):
  def __init__(self, threshold=0.25, metric='euclidean', **kwargs):
    """Samples over a space according to some distribution.

    Subclasses should override self.draw() using some distribution.

    """
    self.threshold = threshold
    self.metric = metric
    super().__init__(**kwargs)

  def draw(self, points, n):
    """Draw points from a given distribution.

    :param points: all the source points to be considered drawing from
    :param n: number of new points to draw
    :returns: numpy array of points

    """
    raise NotImplementedError

  def sample(self, points, n=None):
    """Sample according to the distribution.

    For each randomly sampled point, finds the closest point from points,
    according the given distance metric. For each of these steps, calculates nxN
    pairwise distances, pretty inefficiently.

    TODO: use an Approximate Nearest Neighbor algorithm instead

    :param points: points to sample
    :param n: number of points to take in sampling, overriding sample_size
    :returns: integer array for indexing points, i.e. a "sampling"
    :rtype: 1-D integer array

    """

    N = points.shape[0]
    if n is None:
      n = self.get_sample_size(N) # number of examples to add to sampling
    sampling = np.zeros(N, dtype=np.int64) # starts with zero examples

    while n > 0:
      logger.debug(f"sampler: drawing {n} points")
      draws = self.draw(points, n)
      distances = scipy.spatial.distance.cdist(
        draws, points, metric=self.metric) # (n,N) array of distances

      closest_indices = np.argmin(distances, axis=1)
      closest_distances = np.min(distances, axis=1) # len == n

      # indices to include in the sampling (with possible repetitions)
      indices = closest_indices[closest_distances <= self.threshold]
      for idx in indices:
        sampling[idx] += 1

      n -= indices.shape[0]

    return sampling

class NormalSampler(SpatialSampler):
  def __init__(self, mean=0.0, std=1.0,
               **kwargs):
    """Sample according to a normal distribution, like points"""
    self.mean = mean
    self.std = std
    super().__init__(**kwargs)

  def draw(self, points, n):
    mean = np.mean(points)
    std = np.std(points)
    return np.random.normal(loc=mean, scale=std, size=(n, points.shape[1]))

class MultivariateNormalSampler(SpatialSampler):
  def __init__(self, **kwargs):
    """Sample according to a multivariate normal distribution.

    """
    super().__init__(**kwargs)

  def draw(self, points, n):
    mean = np.mean(points, axis=0)
    cov = np.cov(points, rowvar=False)
    return np.random.multivariate_normal(mean, cov, size=n)

class UniformSampler(SpatialSampler):
  def __init__(self, **kwargs):
    """Samples uniformly over the space covered by points within [low,hi].

    :param threshold: distance threshold at which to resample.
    :param metric: passed to scipy.spatial.distance.cdist

    """
    super().__init__(**kwargs)

  def draw(self, points, n):
    low = np.min(points, axis=0)
    high = np.max(points, axis=0)
    return np.random.uniform(low, high, size=(n, points.shape[1]))

class ClusterSampler(SpatialSampler):
  def __init__(self, clustering=AgglomerativeClustering, n_clusters=5, **kwargs):
    """Draw the same number of points from each cluster.

    Default behavior is to sample uniformly across a cluster, but subclasses can
    modify the draw method to change this.

    :param clustering: clustering class to use
    :param n_clusters: number of clusters, default is 10

    """
    self.n_clusters = n_clusters
    self.clustering = clustering
    super().__init__(**kwargs)

  def cluster(self, points):
    """Return cluster labels for each point in points."""
    logger.info(f"clustering...")
    clustering = self.clustering(self.n_clusters)
    self._cluster_labels = clustering.fit_predict(points)
    return self._cluster_labels

  @property
  def cluster_labels(self):
    return self._cluster_labels

  def sample(self, points):
    """Sample uniformly from clusters in points.

    :param points:
    :returns:
    :rtype:

    """
    N = points.shape[0]
    n = self.get_sample_size(N)
    sampling = np.zeros(N, dtype=np.int64)
    cluster_labels = self.cluster(points)
    for i in range(self.n_clusters):
      cluster_which = cluster_labels == i
      cluster_n = int(round(n * np.sum(cluster_which) / N))
      cluster_points = points[cluster_which]
      cluster_sampling = super().sample(cluster_points, n=cluster_n)
      sampling[cluster_which] += cluster_sampling
    return sampling

class UniformClusterSampler(ClusterSampler, UniformSampler):
  pass

class NormalClusterSampler(ClusterSampler, NormalSampler):
  pass

class MultivariateNormalClusterSampler(ClusterSampler, MultivariateNormalSampler):
  pass


from typing import List

import os
import os.path as osp
import pickle
import numpy as np
import cv2
from easydict import EasyDict
from numpy.lib.function_base import flip

import torch
from torch.utils.data import Dataset, DataLoader

from torch_geometric.data import Data, Batch
import torch_geometric.transforms as T

from datapreparation.kitti360pose.utils import (
    CLASS_TO_LABEL,
    LABEL_TO_CLASS,
    CLASS_TO_MINPOINTS,
    CLASS_TO_INDEX,
    COLORS,
    COLOR_NAMES,
    SCENE_NAMES,
)
from datapreparation.kitti360pose.imports import Object3d, Cell, Pose
from datapreparation.kitti360pose.drawing import show_pptk, show_objects, plot_cell
from dataloading.kitti360pose.base import Kitti360BaseDataset
from dataloading.kitti360pose.utils import batch_object_points, flip_pose_in_cell

def load_pose_and_cell(pose: Pose, cell: Cell, hints, pad_size, transform, args, flip_pose=False):
    assert pose.cell_id == cell.id

    descriptions = pose.descriptions
    cell_objects_dict = {obj.id: obj for obj in cell.objects}
    matched_ids = [descr.object_id for descr in descriptions if descr.is_matched]
    matched_objects = [cell_objects_dict[matched_id] for matched_id in matched_ids]

    assert len(pose.descriptions) == args.num_mentioned
    assert len(pose.descriptions) - pose.get_number_unmatched() == len(matched_objects)

    # Hints and descriptions have to be in same order
    for descr, hint in zip(descriptions, hints):
        assert descr.object_label in hint

    # Gather offsets
    # NOTE: Currently trains on best-offsets if available (matched)!
    if args.regressor_cell == "pose" and args.regressor_learn == "closest":
        offsets = np.array([descr.offset_closest for descr in descriptions])[:, 0:2]
    if args.regressor_cell == "pose" and args.regressor_learn == "center":
        offsets = np.array([descr.offset_center for descr in descriptions])[:, 0:2]
    if args.regressor_cell == "best" and args.regressor_learn == "closest":
        # offsets = np.array([descr.best_offset_closest for descr in descriptions])[:, 0:2]
        offsets = []
        for i_descr, descr in enumerate(descriptions):
            if descr.is_matched:
                offsets.append(descr.best_offset_closest[0:2])
            else:
                offsets.append(descr.offset_closest[0:2])
    if args.regressor_cell == "best" and args.regressor_learn == "center":
        # offsets = np.array([descr.best_offset_center for descr in descriptions])[:, 0:2]
        offsets = []
        for i_descr, descr in enumerate(descriptions):
            if descr.is_matched:
                offsets.append(descr.best_offset_center[0:2])
            else:
                offsets.append(descr.offset_center[0:2])

    offsets = np.array(offsets)

    # Build best-center offsets for oracle
    offsets_best_center = []
    for i_descr, descr in enumerate(descriptions):
        if descr.is_matched:
            offsets_best_center.append(descr.best_offset_center[0:2])
        else:
            offsets_best_center.append(descr.offset_center[0:2])

    offsets_valid = np.sum(np.isnan(offsets)) == 0

    # Gather matched objects
    objects, matches = [], []  # Matches as [(obj_idx, hint_idx)]
    for i_descr, descr in enumerate(descriptions):
        if descr.is_matched:
            hint_obj = cell_objects_dict[descr.object_id]
            assert hint_obj.instance_id == descr.object_instance_id
            objects.append(hint_obj)

            obj_idx = len(objects) - 1
            hint_idx = i_descr
            matches.append((obj_idx, hint_idx))

    # Gather distractors, i.e. remaining objects
    for obj in cell.objects:
        if obj.id not in matched_ids:
            objects.append(obj)
    if len(objects) != len(cell.objects):
        print([obj.id for obj in objects])
        print([obj.id for obj in cell.objects])
        print(matched_ids)
    assert len(objects) == len(
        cell.objects
    ), f"Not all cell-objects have been gathered! {len(objects)}, {len(cell.objects)}, {cell.id}"

    # Pad or cut-off distractors (CARE: the latter would use ground-truth data!)
    if len(objects) > pad_size:
        objects = objects[0:pad_size]

    while len(objects) < pad_size:
        obj = Object3d.create_padding()
        objects.append(obj)

    # Build matches and all_matches
    # The matched objects are always put in first, however, our geometric models have no knowledge of these indices as they are permutation invariant.
    all_matches = matches.copy()

    # Add unmatched hints
    for i_descr, descr in enumerate(descriptions):
        if not descr.is_matched:
            obj_idx = len(objects)  # Match to objects-side bin
            hint_idx = i_descr
            all_matches.append((obj_idx, hint_idx))

    # Add unmatched objects
    for obj_idx, obj in enumerate(objects):
        if obj.id not in matched_ids:
            hint_idx = len(descriptions)  # Match to hints-side bin
            all_matches.append((obj_idx, hint_idx))

    matches, all_matches = np.array(matches), np.array(all_matches)
    assert len(matches) == len(matched_ids)
    assert len(all_matches) == len(objects) + len(descriptions) - len(matches)
    assert np.sum(all_matches[:, 1] == len(descriptions)) == len(objects) - len(
        matched_ids
    )  # Binned objects
    assert np.sum(all_matches[:, 0] == len(objects)) == len(descriptions) - len(
        matched_ids
    )  # Binned hints

    text = " ".join(hints)

    # TODO: flip here. This does not affect the the order and matches.
    # Flip objects, hint_descriptions, object_points, offsets, pose
    if flip_pose:
        if np.random.choice((True, False)):
            pose, cell, text, hints, offsets = flip_pose_in_cell(
                pose, cell, text, 1, hints, offsets
            )  # Horizontal
        if np.random.choice((True, False)):
            pose, cell, text, hints, offsets = flip_pose_in_cell(
                pose, cell, text, -1, hints, offsets
            )  # Vertical

    object_points = batch_object_points(objects, transform)

    object_class_indices = [CLASS_TO_INDEX[obj.label] for obj in objects]
    object_color_indices = [COLOR_NAMES.index(obj.get_color_text()) for obj in objects]

    return {
        "poses": pose,
        "cells": cell,
        "objects": objects,
        "object_points": object_points,
        "hint_descriptions": hints,
        "texts": text,
        "matches": matches,
        "all_matches": all_matches,
        "offsets": np.array(offsets),
        "offsets_best_center": np.array(offsets_best_center),
        # 'offsets_valid': offsets_valid,
        "object_class_indices": object_class_indices,
        "object_color_indices": object_color_indices,
    }

class Kitti360FineDataset(Kitti360BaseDataset):
    def __init__(self, base_path, scene_name, transform, args, flip_pose=False):
        """Dataset to train the fine module.

        Args:
            base_path: Data root path
            scene_name: Scene name
            transform: PyG transform on object points
            args: Global training arguments
            flip_pose (bool, optional): Flip poses to opposite site of the cell (including the hint direction). Defaults to False.
        """
        super().__init__(base_path, scene_name)
        self.pad_size = args.pad_size
        self.transform = transform
        self.flip_pose = flip_pose
        self.args = args

    def __getitem__(self, idx):
        pose = self.poses[idx]
        cell = self.cells_dict[pose.cell_id]
        hints = self.hint_descriptions[idx]

        return load_pose_and_cell(
            pose, cell, hints, self.pad_size, self.transform, self.args, flip_pose=self.flip_pose
        )

    def __len__(self):
        return len(self.poses)

    def collate_fn(data):
        batch = {}
        for key in data[0].keys():
            batch[key] = [data[i][key] for i in range(len(data))]
        return batch

class Kitti360FineDatasetMulti(Dataset):
    def __init__(self, base_path, scene_names, transform, args, flip_pose=False):
        """Multi-scene version of Kitti360FineDataset.

        Args:
            base_path: Data root path
            scene_name: Scene name
            transform: PyG transform on object points
            args: Global training arguments
            flip_pose (bool, optional): Flip poses to opposite site of the cell (including the hint direction). Defaults to False.
        """
        self.scene_names = scene_names
        self.flip_pose = flip_pose
        self.datasets = [
            Kitti360FineDataset(base_path, scene_name, transform, args, flip_pose)
            for scene_name in scene_names
        ]

        self.all_poses = [pose for dataset in self.datasets for pose in dataset.poses]  # For stats
        self.all_cells = [
            cell for dataset in self.datasets for cell in dataset.cells
        ]  # For eval stats

        print(str(self))

    def __getitem__(self, idx):
        count = 0
        for dataset in self.datasets:
            idx_in_dataset = idx - count
            if idx_in_dataset < len(dataset):
                return dataset[idx_in_dataset]
            else:
                count += len(dataset)
        assert False

    def __repr__(self):
        poses = np.array([pose.pose_w for pose in self.all_poses])
        num_poses = len(
            np.unique(poses, axis=0)
        )  # CARE: Might be possible that is is slightly inaccurate if there are actually overlaps
        return f"Kitti360FineDatasetMulti: {len(self)} descriptions for {num_poses} unique poses from {len(self.datasets)} scenes, {len(self.all_cells)} cells, flip: {self.flip_pose}."

    def __len__(self):
        return np.sum([len(ds) for ds in self.datasets])

    def get_known_words(self):
        known_words = []
        for ds in self.datasets:
            known_words.extend(ds.get_known_words())
        return list(np.unique(known_words))

    def get_known_classes(self):
        known_classes = []
        for ds in self.datasets:
            known_classes.extend(ds.get_known_classes())
        return list(np.unique(known_classes))

if __name__ == "__main__":
    base_path = "./data/k360_30-10_scG_pd10_pc4_spY_all"
    folder_name = "2013_05_28_drive_0003_sync"

    args = EasyDict(pad_size=8, num_mentioned=6, ranking_loss="pairwise", regressor_cell="pose", regressor_learn="center", regressor_eval="center")
    transform = T.Compose([T.FixedPoints(1024), T.NormalizeScale()])

    dataset = Kitti360FineDatasetMulti(
        base_path,
        [
            folder_name,
        ],
        transform,
        args,
    )
    data = dataset[0]


# Frenet Nonlinear Kinematic MPC Module.
# Currently only supports a fixed velocity setpoint (initial value of v_ref).

import time
import casadi
import numpy as np
from controller import Controller

class KinFrenetMPCPathFollower(Controller):
	##
	def __init__(self,
		         N          = 10,            # timesteps in MPC Horizon
		         DT       	= 0.2,           # discretization time between timesteps (s)
		         L_F        = 1.5213,        # distance from CoG to front axle (m)
		         L_R        = 1.4987,        # distance from CoG to rear axle (m)
				 AX_MAX     =  5.0,
		         AX_MIN     = -10.0,         # min/max longitudinal acceleration constraint (m/s^2)
				 AY_MAX     =  3.0,
				 AY_MIN     = -3.0,          # min/max lateral acceleration constraint (m/s^2)
		         DF_MAX     =  30*np.pi/180,
		         DF_MIN     = -30*np.pi/180, # min/max front steer angle constraint (rad)
		         AX_DOT_MAX  =  1.5,
		         AX_DOT_MIN  = -1.5,          # min/max longitudinal jerk constraint (m/s^3)
				 AY_DOT_MAX  =  5.,
		         AY_DOT_MIN  = -5.,          # min/max lateral jerk constraint (m/s^3)
		         DF_DOT_MAX =  30*np.pi/180,
		         DF_DOT_MIN = -30*np.pi/180, # min/max front steer angle rate constraint (rad/s)
		         EY_MAX		=  0.8,
				 EY_MIN		= -0.8,          # min/max lateral error bounds (m)
				 EPSI_MAX	=  10*np.pi/180,
				 EPSI_MIN	= -10*np.pi/180, # min/max heading error bounds (rad)
				 Q = [0., 100., 500., 1.],   # weights on s, ey, epsi, v
				 R = [.01, .001]):           # input rate weights on ax, df

		for key in list(locals()):
			if key == 'self':
				pass
			elif key == 'Q':
				self.Q = casadi.diag(Q)
			elif key == 'R':
				self.R = casadi.diag(R)
			else:
				setattr(self, '%s' % key, locals()[key])

		self.opti = casadi.Opti()

		'''
		(1) Parameters
		'''
		self.u_prev  = self.opti.parameter(2) # previous input: [u_{acc, -1}, u_{df, -1}]
		self.z_curr  = self.opti.parameter(4) # current state:  [s0, ey_0, epsi_0, v_0]

		# Velocity and Curvature Profile.
		self.curv_ref = self.opti.parameter(self.N)
		self.v_ref    = self.opti.parameter()
		self.z_ref    = casadi.horzcat(casadi.MX.zeros(1,3), self.v_ref)

		'''
		(2) Decision Variables
		'''
		## First index is the timestep k, i.e. self.z_dv[0,:] is z_0.
		## It has self.N+1 timesteps since we go from z_0, ..., z_self.N.
		## Second index is the state element, as detailed below.
		self.z_dv = self.opti.variable(self.N+1, 4) # s, ey, epsi, v

		self.s_dv    = self.z_dv[:, 0]
		self.ey_dv   = self.z_dv[:, 1]
		self.epsi_dv = self.z_dv[:, 2]
		self.v_dv    = self.z_dv[:, 3]

		## Control inputs used to achieve self.z_dv according to dynamics.
		## First index is the timestep k, i.e. self.u_dv[0,:] is u_0.
		## Second index is the input element as detailed below.
		self.u_dv = self.opti.variable(self.N, 2)

		self.acc_dv = self.u_dv[:,0]
		self.df_dv  = self.u_dv[:,1]

		# Slack variables used for lateral acceleration constraints.
		self.sl_ay_dv  = self.opti.variable(self.N)

		'''
		(3) Problem Setup: Constraints, Cost, Initial Solve
		'''
		self._add_constraints()

		self._add_cost()

		self._update_initial_condition(0., 0., 0., 1.)

		self._update_reference(self.N*[0.], 5.)

		self._update_previous_input(0., 0.)

		self.global_state = np.array([0., 0., 0., 0.])
		self.global_ref   = np.zeros((N+1, 4))

		# Ipopt with custom options: https://web.casadi.org/docs/ -> see sec 9.1 on Opti stack.
		p_opts = {'expand': True}
		s_opts = {'max_cpu_time': 0.1, 'print_level': 0}
		self.opti.solver('ipopt', p_opts, s_opts)

		sol = self.solve()

	def _add_constraints(self):
		## State Bound Constraints
		self.opti.subject_to( self.opti.bounded(self.EY_MIN, self.ey_dv, self.EY_MAX) )
		self.opti.subject_to( self.opti.bounded(self.EPSI_MIN, self.epsi_dv, self.EPSI_MAX) )

		## Initial State Constraint
		self.opti.subject_to( self.s_dv[0]    == self.z_curr[0] )
		self.opti.subject_to( self.ey_dv[0]   == self.z_curr[1] )
		self.opti.subject_to( self.epsi_dv[0] == self.z_curr[2] )
		self.opti.subject_to( self.v_dv[0]    == self.z_curr[3] )

		## State Dynamics Constraints
		for i in range(self.N):
			beta = casadi.atan( self.L_R / (self.L_F + self.L_R) * casadi.tan(self.df_dv[i]) )
			dyawdt = self.v_dv[i] / self.L_R * casadi.sin(beta)
			dsdt = self.v_dv[i] * casadi.cos(self.epsi_dv[i]+beta) / (1 - self.ey_dv[i] * self.curv_ref[i] )
			ay   = self.v_dv[i] * dyawdt

			self.opti.subject_to( self.s_dv[i+1]    == self.s_dv[i]    + self.DT * (dsdt) )
			self.opti.subject_to( self.ey_dv[i+1]   == self.ey_dv[i]   + self.DT * (self.v_dv[i] * casadi.sin(self.epsi_dv[i] + beta)) )
			self.opti.subject_to( self.epsi_dv[i+1] == self.epsi_dv[i] + self.DT * (dyawdt - dsdt * self.curv_ref[i]) )
			self.opti.subject_to( self.v_dv[i+1]    == self.v_dv[i]    + self.DT * (self.acc_dv[i]) )

			self.opti.subject_to( self.opti.bounded(self.AY_MIN - self.sl_ay_dv[i],
				                                    ay,
				                                    self.AY_MAX + self.sl_ay_dv[i]) )

		## Input Bound Constraints
		self.opti.subject_to( self.opti.bounded(self.AX_MIN, self.acc_dv, self.AX_MAX) )
		self.opti.subject_to( self.opti.bounded(self.DF_MIN, self.df_dv,  self.DF_MAX) )

		# Input Rate Bound Constraints
		self.opti.subject_to( self.opti.bounded( self.AX_DOT_MIN*self.DT,
			                                     self.acc_dv[0] - self.u_prev[0],
			                                     self.AX_DOT_MAX*self.DT) )

		self.opti.subject_to( self.opti.bounded( self.DF_DOT_MIN*self.DT,
			                                     self.df_dv[0] - self.u_prev[1],
			                                     self.DF_DOT_MAX*self.DT) )

		for i in range(self.N - 1):
			self.opti.subject_to( self.opti.bounded( self.AX_DOT_MIN*self.DT,
				                                     self.acc_dv[i+1] - self.acc_dv[i],
				                                     self.AX_DOT_MAX*self.DT) )
			self.opti.subject_to( self.opti.bounded( self.DF_DOT_MIN*self.DT,
				                                     self.df_dv[i+1]  - self.df_dv[i],
				                                     self.DF_DOT_MAX*self.DT) )
		# Other Constraints
		self.opti.subject_to( 0 <= self.sl_ay_dv ) # nonnegative lateral acceleration slack variable

	## Cost function
	def _add_cost(self):
		def _quad_form(z, Q):
			return casadi.mtimes(z, casadi.mtimes(Q, z.T))

		cost = 0
		for i in range(self.N):
			cost += _quad_form(self.z_dv[i+1, :]-self.z_ref, self.Q)

		for i in range(self.N - 1):
			cost += _quad_form(self.u_dv[i+1, :] - self.u_dv[i,:], self.R)

		cost += casadi.sum1(self.sl_ay_dv)
		self.opti.minimize( cost )

	def _get_global_trajectory(self, u_mpc):
		z_mpc = np.ones((self.N+1, 4)) * np.nan
		z_mpc[0, :] = self.global_state

		for ind, u in enumerate(u_mpc):
			x, y, p, v = z_mpc[ind, :]
			u_acc, u_df = u

			beta = np.arctan( self.L_R / (self.L_F + self.L_R) * np.tan(u_df) )

			xn = x + self.DT * (v * np.cos(p + beta))
			yn = y + self.DT * (v * np.sin(p + beta))
			pn = p + self.DT * (v / self.L_R * np.sin(beta))
			vn = v + self.DT * (u_acc)

			z_mpc[ind+1, :] = [xn, yn, pn, vn]

		return z_mpc

	def solve(self):
		st = time.time()
		try:
			sol = self.opti.solve()
			# Optimal solution.
			u_mpc    = sol.value(self.u_dv)
			z_mpc    = sol.value(self.z_dv)
			sl_mpc   = sol.value(self.sl_ay_dv)
			curv_ref = sol.value(self.curv_ref)
			v_ref    = sol.value(self.v_ref)
			is_opt = True
		except:
			# Suboptimal solution (e.g. timed out).
			u_mpc    = self.opti.debug.value(self.u_dv)
			z_mpc    = self.opti.debug.value(self.z_dv)
			sl_mpc   = self.opti.debug.value(self.sl_ay_dv)
			curv_ref = self.opti.debug.value(self.curv_ref)
			v_ref    = self.opti.debug.value(self.v_ref)
			is_opt   = False

		solve_time = time.time() - st

		sol_dict = {}
		sol_dict['u_control']    = u_mpc[0,:]      # control input to apply based on solution
		sol_dict['optimal']      = is_opt          # whether the solution is optimal or not
		sol_dict['solve_time']   = solve_time      # how long the solver took in seconds
		sol_dict['u_mpc']        = u_mpc           # solution inputs (N by 2, see self.u_dv above)
		sol_dict['z_mpc_frenet'] = z_mpc           # solution states (N+1 by 4, see self.z_dv above)
		sol_dict['sl_mpc']       = sl_mpc          # solution slack vars (N by 1, see self.sl_ay_dv above)

		sol_dict['v_ref_frenet']    = v_ref        # velocity reference (scalar)
		sol_dict['curv_ref_frenet'] = curv_ref     # curvature reference (N by 1)

		sol_dict['z_mpc'] = self._get_global_trajectory(u_mpc) # get MPC solution trajectory in global frame (N+1, 4)
		sol_dict['z_ref'] = self.global_ref                    # state reference (N by 4)

		return sol_dict

	def update(self, update_dict):
		self.global_state = np.array( [update_dict[key] for key in ['x0', 'y0', 'psi0', 'v0']] )
		self.global_ref   = np.column_stack(( update_dict['x_ref'],
			                                  update_dict['y_ref'],
			                                  update_dict['psi_ref'],
			                                  update_dict['v_ref'] ))

		self._update_initial_condition( *[update_dict[key] for key in ['s0', 'e_y0', 'e_psi0', 'v0']] )

		# Calculate and apply a velocity reference limit from the upcoming curvatures.
		# This is designed assuming the velocity setpoint is constant and not time-varying.
		v_desired     = update_dict['v_ref'][0]
		curvature_max = np.amax(np.fabs(update_dict['curv_ref']))
		v_limit       = np.sqrt(self.AY_MAX/np.fabs(curvature_max))

		self._update_reference( update_dict['curv_ref'],
			                    np.clip(v_desired, -v_limit, v_limit ))

		self._update_previous_input( *[update_dict[key] for key in ['acc_prev', 'df_prev']] )

		# TODO: clean up warm start by unifying the keys in sol_dict and update_dict.
		# if 'warm_start' in update_dict.keys():
		# 	# Warm Start used if provided.  Else I believe the problem is solved from scratch with initial values of 0.
		# 	self.opti.set_initial(self.z_dv,  update_dict['warm_start']['z_ws'])
		# 	self.opti.set_initial(self.u_dv,  update_dict['warm_start']['u_ws'])
		# 	self.opti.set_initial(self.sl_dv, update_dict['warm_start']['sl_ws'])

	def _update_initial_condition(self, s0, ey0, epsi0, vel0):
		self.opti.set_value(self.z_curr, [s0, ey0, epsi0, vel0])

	def _update_reference(self, curv_ref, v_ref):
		self.opti.set_value(self.curv_ref, curv_ref)
		self.opti.set_value(self.v_ref, v_ref)

	def _update_previous_input(self, acc_prev, df_prev):
		self.opti.set_value(self.u_prev, [acc_prev, df_prev])

if __name__ == '__main__':
	kmpc = KinFrenetMPCPathFollower()
	sol_dict = kmpc.solve()

	np.set_printoptions(precision=3)
	for key in sol_dict:
		print(key, sol_dict[key])


import tkinter as tk
from tkinter import messagebox
import sqlite3

# Create a database connection
conn = sqlite3.connect("library.db")
c = conn.cursor()

# Create the main application window
root = tk.Tk()
root.title("Reporting")
root.geometry("400x300")
root.config(bg="lightgray")

# Function to generate the list of available books report
def generate_available_books_report():
    c.execute("SELECT * FROM books WHERE id NOT IN (SELECT book_id FROM borrowings WHERE return_date IS NULL)")
    books = c.fetchall()

    report_text = ""
    if books:
        report_text = "Available Books:\n"
        for book in books:
            report_text += f"Title: {book[1]}\nAuthor: {book[2]}\nISBN: {book[3]}\nPublication Date: {book[4]}\n\n"
    else:
        report_text = "No available books."

    # Display the report in a message box
    messagebox.showinfo("Available Books Report", report_text)

# Function to generate the list of borrowed books report
def generate_borrowed_books_report():
    c.execute("SELECT * FROM books WHERE id IN (SELECT book_id FROM borrowings WHERE return_date IS NULL)")
    books = c.fetchall()

    report_text = ""
    if books:
        report_text = "Borrowed Books:\n"
        for book in books:
            report_text += f"Title: {book[1]}\nAuthor: {book[2]}\nISBN: {book[3]}\nPublication Date: {book[4]}\n\n"
    else:
        report_text = "No borrowed books."

    # Display the report in a message box
    messagebox.showinfo("Borrowed Books Report", report_text)

# Function to generate the list of overdue books report
def generate_overdue_books_report():
    c.execute("SELECT * FROM books WHERE id IN (SELECT book_id FROM borrowings WHERE return_date IS NULL AND due_date < DATE('now'))")
    books = c.fetchall()

    report_text = ""
    if books:
        report_text = "Overdue Books:\n"
        for book in books:
            report_text += f"Title: {book[1]}\nAuthor: {book[2]}\nISBN: {book[3]}\nPublication Date: {book[4]}\n\n"
    else:
        report_text = "No overdue books."

    # Display the report in a message box
    messagebox.showinfo("Overdue Books Report", report_text)

# Generate Available Books Report button
btn_available_books_report = tk.Button(root, text="Generate Available Books Report", command=generate_available_books_report, bg="lightgray", fg="black", font=("Arial", 12))
btn_available_books_report.pack()

# Generate Borrowed Books Report button
btn_borrowed_books_report = tk.Button(root, text="Generate Borrowed Books Report", command=generate_borrowed_books_report, bg="lightgray", fg="black", font=("Arial", 12))
btn_borrowed_books_report.pack()

# Generate Overdue Books Report button
btn_overdue_books_report = tk.Button(root, text="Generate Overdue Books Report", command=generate_overdue_books_report, bg="lightgray", fg="black", font=("Arial", 12))
btn_overdue_books_report.pack()

# Run the Tkinter event loop
root.mainloop()

# Close the database connection when the application is closed
conn.close()


#!/usr/bin/env python3
"""
Copy a gear test using hard links for the files to avoid wasting a
lot of space.  The files need to be hard links so they will show
up in the Docker container (symbolic links show up as symbolic links
to nowhere).
"""

import os
import shutil
import subprocess as sp
import argparse

def copy(src, dst):
    """ Copies a test directory, deletes all old log files.  """

    if os.path.isdir(src):

        try:
            # here's the beef:
            shutil.copytree(src, dst, copy_function=os.link)

        except Exception as e:
            LOG.error(e)
            print(e)
            msg='WARNING: continuing despite errors'
            print(msg)
            LOG.info('WARNING: continuing despite errors')

        # and remove the old test's logs (if any)
        shutil.rmtree(dst+'/logs')
        os.mkdir(dst+'/logs')

        # delete the hard-linked config.json and actually
        # copy it so it can be edited without modifying the
        # old config.
        os.remove(dst + '/config.json')
        shutil.copyfile(src + '/config.json',dst + '/config.json')

        msg = 'Copied test from\n  ' + src + '\nto\n  ' + dst
        print(msg)
        LOG.info(msg)

        msg = 'NOTE: all files were copied as HARD links except for \n'+\
              '"confid.json".  Unless you want to modify a file in the \n'+\
              'old test that was copied, DELETE the file and create a new one'
        print(msg)

    else:
        print('ERROR: "'+args.src+'" not found.  It should be in')
        print('ls '+TEST+'tests/')
        result = sp.run(['ls',TEST+'tests/'])

def init_by_copying():
    """ This is called from setup.py.  To let the user knonw that
    they can call copy_test.py from the command line, this does just that
    This is a whole lot of wrapping around the tiny bit of code above """

    print("Copying an existing test...\n")

    msg = "Choose a test to copy:\n"
    tests = os.listdir(TEST+'tests/')
    nn = 1
    dir_name = dict()
    choices = []
    for tt in tests:
        if tt[0] != '.' and tt[0] != '_':
            msg += "  "+str(nn)+") "+tt+"\n"
            dir_name[nn] = tt
            choices.append(str(nn))
            nn += 1

    not_done = True

    while not_done:

        ans = get_user_input(msg,"Which would you like to use?",choices)

        new_name = input('Please enter a name for the new test: ')

        print("The new test will be created with this command:\n")
        cmd = 'utils/copy_test.py '+dir_name[int(ans)]+' '+new_name
        print('  '+cmd+'\n')
        print('(You can run that command yourself instead of this script)')

        go_nogo = get_user_input("","",['Proceed','Re-enter','Quit?'])

        if go_nogo == 'p':
            not_done = False
        elif go_nogo == 'q':
            sys.exit()

    command = TEST+cmd # add full path
    print('Running: '+command)
    LOG.info('Running" '+command)
    command = [ w for w in command.split() ]
    result = sp.run(command)

if __name__ == '__main__':

    from find_gear import *
    from get_user_input import *

    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("src",help="(old) source test direcory name (no path)")
    parser.add_argument("dst",help="(new) destination directory name")
    args = parser.parse_args()

    src = TEST+'tests/'+args.src
    dst = TEST+'tests/'+args.dst

    copy(src,dst)

else:

    from .find_gear import *
    from .get_user_input import *

# vi:set autoindent ts=4 sw=4 expandtab : See Vim, :help 'modeline'


import os
import numpy as np
import json
import matplotlib.pyplot as plt
import cv2

import vstarstack.library.data
import vstarstack.tool.cfg
import vstarstack.tool.cfg

def show(project: vstarstack.tool.cfg.Project, argv: list):
    """Display detected stars"""
    image = vstarstack.library.data.DataFrame.load(argv[0])
    channel = argv[1]
    with open(argv[2], encoding='utf8') as f:
        descs = json.load(f)
    show_all = vstarstack.tool.cfg.get_param("all", bool, False)
    slope = vstarstack.tool.cfg.get_param("multiply", float, 1)
    layer, _ = image.get_channel(channel)
    layer = np.clip(layer/np.amax(layer)*slope, 0, 1)
    layer = (layer * 255).astype(np.uint8)

    showed = np.zeros((layer.shape[0], layer.shape[1], 3), dtype="uint8")
    showed[:,:,0] = layer
    showed[:,:,1] = layer
    showed[:,:,2] = layer
    if show_all:
        items = descs["stars"]
    else:
        items = [item["star"] for item in descs["main"]]
    for star in items:
        cv2.circle(showed, (star["x"], star["y"]), int(star["size"]+11), (255,0,0), 1)

    plt.imshow(showed)
    plt.show()

def get_brightness(df):
    light = None
    for channel in df.get_channels():
        layer, opts = df.get_channel(channel)
        if not opts["brightness"]:
            continue
        if light is None:
            light = layer
        else:
            light = light + layer
    return light

def show_match(project: vstarstack.tool.cfg.Project, argv: list):
    """Display detected stars"""
    fname1 = argv[0]
    fname2 = argv[1]
    name1 = os.path.splitext(os.path.basename(fname1))[0]
    name2 = os.path.splitext(os.path.basename(fname2))[0]
    image1 = vstarstack.library.data.DataFrame.load(fname1)
    image2 = vstarstack.library.data.DataFrame.load(fname2)
    with open(argv[2], encoding='utf8') as f:
        descs1 = json.load(f)
    with open(argv[3], encoding='utf8') as f:
        descs2 = json.load(f)
    with open(argv[4], encoding='utf8') as f:
        match_table = json.load(f)

    slope = vstarstack.tool.cfg.get_param("multiply", float, 1)
    layer1 = get_brightness(image1)
    layer2 = get_brightness(image2)
    layer1 = np.clip(layer1/np.amax(layer1)*slope, 0, 1)
    layer1 = (layer1 * 255).astype(np.uint8)
    layer2 = np.clip(layer2/np.amax(layer2)*slope, 0, 1)
    layer2 = (layer2 * 255).astype(np.uint8)

    kps1 = []
    kps2 = []
    matches = []
    index = 0
    for star1_id, star2_id in match_table[name1][name2].items():
        star1_id = int(star1_id)
        star1 = descs1["main"][star1_id]["star"]
        star2 = descs2["main"][star2_id]["star"]
        kps1.append(cv2.KeyPoint(star1["x"], star1["y"], int(star1["radius"])+2))
        kps2.append(cv2.KeyPoint(star2["x"], star2["y"], int(star2["radius"])+2))
        matches.append(cv2.DMatch(index, index, 0))
        index += 1

    img3 = cv2.drawMatches(layer1, kps1, layer2, kps2,
                           matches, None,
                           flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
    plt.imshow(img3)
    plt.show()


from playwright.sync_api import Playwright, sync_playwright, expect

def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()

    page = context.new_page()

    page.goto("https://einstructor.datasena.com/login")

    page.get_by_placeholder("Número Identificación").click()

    page.get_by_placeholder("Número Identificación").fill("1014737507")

    page.get_by_placeholder("Contraseña").click()

    page.get_by_placeholder("Contraseña").fill("1014737507")

    page.get_by_role("button", name="Ingresar").click()
    page.wait_for_url("https://einstructor.datasena.com/home")

    page.get_by_role("link", name=" Ejecutar evaluación").click()
    page.wait_for_url("https://einstructor.datasena.com/assessment")

    page.get_by_label("Digite el código de la evaluación").click()

    page.get_by_label("Digite el código de la evaluación").fill("FY9rqS2vxZ")

    page.get_by_role("button", name="Confirmar").click()

    page.locator(".btn").first.click()

    page.locator("#answers1").get_by_text("Siempre").click()

    page.locator("#answers2").get_by_text("Siempre").click()

    page.locator("#answers3").get_by_text("Siempre").click()

    page.locator("#answers4").get_by_text("Siempre").click()

    page.locator("#answers5").get_by_text("Siempre").click()

    page.locator("#answers6").get_by_text("Siempre").click()

    page.locator("#answers7").get_by_text("Siempre").click()

    page.locator("#answers8").get_by_text("Siempre").click()

    page.locator("#answers9").get_by_text("Siempre").click()

    page.locator("#answers10").get_by_text("Siempre").click()

    page.locator("#answers11").get_by_text("Siempre").click()

    page.locator("#answers12").get_by_text("Siempre").click()

    page.get_by_role("button", name="Enviar").click()

    page.get_by_role("button", name="Aceptar").click()

    page.get_by_role("link").nth(1).click()
    page.wait_for_url("https://einstructor.datasena.com/")

    # ---------------------
    context.close()
    browser.close()

with sync_playwright() as playwright:
    run(playwright)


import openpyxl as pxl
wdict = pxl.load_workbook('C:/Users/m.shcherbina/PycharmProjects/ITOIUItransfer/dictionary_simple.xlsx')
wSdict = wdict.active
wbook = pxl.load_workbook('C:/Users/m.shcherbina/PycharmProjects/ITOIUItransfer/For_powershell_task_3_output.xlsx')
wSbook = wbook.active

replacedict = {}
for i in range(1, wSdict.max_row + 1):

    cell_value_class = wSdict.cell(i, 1).value
    cell_value_id = wSdict.cell(i, 2).value
    replacedict[cell_value_id] = cell_value_class
#    define[cell_value_id] = cell_value_class
#    replacedict.append(define)
print(replacedict)
print(wSdict.max_row)
j=0
for i in range(1, wSbook.max_row + 1):
   for key in replacedict:
      if wSbook.cell(i, 1).value == key:
        newCell = replacedict[key]
        wSbook.cell(i, 1).value = newCell
        j += 1
        print('Выполнена замена', key, 'на', replacedict[key], 'в строке', i)
wbook.save('C:/Users/m.shcherbina/PycharmProjects/ITOIUItransfer/output.xlsx')
print('Файл сохранен, выполнено', j, 'замен')


import pygame as pg
import math
from Tower import Tower
import numpy as np

class Board:
    def __init__(self,screenSizeX,screenSizeY):
        self.screenSizeX = screenSizeX
        self.screenSizeY = screenSizeY
        self.screen = pg.display.set_mode((screenSizeX, screenSizeY))
        self.nbSquare = 8
        self.colors = {'orange':'#CD6C37','pink':'#D85EC1','red':'#CD325A','green':'#029561','blue':'#137CCF','yellow':'#C9B91C','brown':'#472C17','purple':'#7949B5'}
        self.squareSizeX = int(self.screenSizeX/self.nbSquare)
        self.squareSizeY = int(self.screenSizeY / self.nbSquare)
        self.p0Towers = []
        self.p1Towers = []
        self.board = self.initBoardColor()
        self.currentTurn=0
        self.playerTurn=0
        self.selectedTower=None
        self.lastTurnColor=None
        self.initPlayers()

    def initBoardColor(self):
        return [['orange','blue','purple','pink','yellow','red','green','brown'],
                ['red','orange','pink','green','blue','yellow','brown','purple'],
                ['green', 'pink', 'orange', 'red','purple', 'brown', 'yellow', 'blue'],
                ['pink', 'purple', 'blue', 'orange','brown', 'green', 'red', 'yellow'],
                ['yellow', 'red', 'green', 'brown','orange', 'blue', 'purple', 'pink'],
                ['blue', 'yellow', 'brown', 'purple','red', 'orange', 'pink', 'green'],
                ['purple', 'brown', 'yellow', 'blue','green', 'pink', 'orange', 'red'],
                ['brown', 'green', 'red', 'yellow','pink', 'purple', 'blue', 'orange']]

    def initPlayers(self):

        p0Towers = []
        p1Towers = []
        p0Color = '#ffffff'
        p1Color = '#000000'
        i = 0
        colors = ['orange','blue','purple','pink','yellow','red','green','brown']
        for color in colors:
            p0Towers.append(Tower(0,self.colors[color],color,p0Color,i,0))
            i+=1
        self.p0Towers = p0Towers
        i=0
        for color in colors.__reversed__():
            p1Towers.append(Tower(1, self.colors[color],color, p1Color, i,7))
            i += 1
        self.p1Towers = p1Towers

    def displayPlayers(self):
        for tower in self.p0Towers+self.p1Towers:
            if self.selectedTower != None:
                if self.selectedTower.x == tower.x and self.selectedTower.y == tower.y:
                    pg.draw.circle(self.screen, pg.Color('#FF0000'),
                                   (tower.x * self.squareSizeX + self.squareSizeX / 2,
                                    tower.y * self.squareSizeY + self.squareSizeY / 2),
                                   self.squareSizeX /3)
            pg.draw.circle(self.screen, tower.playerColor, (tower.x*self.squareSizeX+self.squareSizeX/2,
                                                       tower.y*self.squareSizeY+self.squareSizeY/2),
                           self.squareSizeX/4)
            pg.draw.circle(self.screen, tower.color, (tower.x * self.squareSizeX + self.squareSizeX / 2,
                                                       tower.y * self.squareSizeY + self.squareSizeY / 2),
                           self.squareSizeX/6)

    def displayBoard(self):
        for i in range(self.nbSquare):
            for j in range(self.nbSquare):
                color = pg.Color(self.colors[self.board[j][i]])
                pg.draw.rect(self.screen,color,pg.Rect(i*self.squareSizeX,j*self.squareSizeY,(i+1)*self.squareSizeX,(j+1)*self.squareSizeY))

    def displayAll(self):
        self.screen.fill((255, 255, 255))
        self.displayBoard()
        self.displayPlayers()
        pg.display.flip()

    def victoryCheck(self):
        for tower in self.p0Towers:
            if tower.y == 7:
                print('player 1 WIN !!! Congrats !')
                return 1
        for tower in self.p1Towers:
            if tower.y == 0:
                print('player 0 WIN !!! Congrats !')
                return 0
        return -1

    def handlePlayerClick(self,pos):
        if self.selectedTower == None and self.currentTurn==0:
            self.playerSelectTower(pos)
        else:
            self.playerSelectDestination(pos)

    def findTowerPerCoordinates(self,playerIndex,x,y):
        if playerIndex == 0:
            for tower in self.p0Towers:
                if tower.x == x and tower.y == y:
                    return tower
        else:
            for tower in self.p1Towers:
                if tower.x == x and tower.y == y:
                    return tower
        return None
    def playerSelectTower(self,pos):
        x = math.trunc(pos[0]/self.squareSizeX)
        y = math.trunc(pos[1]/self.squareSizeY)
        selectedSquare = self.findTowerPerCoordinates(self.playerTurn,x,y)
        if selectedSquare != None:
            if selectedSquare.indexPlayer == self.playerTurn:
                self.selectedTower = selectedSquare

    def changeTurn(self):
        if self.playerTurn == 0:
            self.playerTurn = 1
        else:
            self.playerTurn = 0
        self.currentTurn+=1

    def isAbleToMove(self,tower):
        if tower.indexPlayer == 0:
            coordToCheck = [(tower.y+1,tower.x),]
            if tower.x-1 > 0:
                coordToCheck.append(((tower.x-1,tower.y+1)))
            if tower.x+1<self.nbSquare:
                coordToCheck.append(((tower.x + 1, tower.y + 1)))
        else:
            coordToCheck = [(tower.x, tower.y - 1)]
            if tower.x-1 > 0:
                coordToCheck.append(((tower.x-1,tower.y-1)))
            if tower.x+1<self.nbSquare:
                coordToCheck.append(((tower.x + 1, tower.y -1)))

        nbBlock = 0
        for coord in coordToCheck:
            alreadyCheckedThisOne = False
            for testedTower in self.p0Towers+self.p1Towers:
                if testedTower.x == coord[0] and testedTower.y == coord[1] and not alreadyCheckedThisOne:
                    nbBlock+=1
                    alreadyCheckedThisOne = True

        if nbBlock == len(coordToCheck):
            return False
        else:
            return True

    def isTHereATowerInDiagonale(self,tower,destY,destX):
        difXGlobal = abs(destX-tower.x)
        difYGlobal = abs(destY-tower.y)
        if difYGlobal != difXGlobal :
            return True

        for checkTower in self.p0Towers+self.p1Towers:
            if checkTower.x != tower.x or checkTower.y != tower.y :
                difX = abs(checkTower.x-tower.x)
                difY = abs(checkTower.y-tower.y)
                if difY == difX:
                    if tower.indexPlayer == 0:
                        inFront = checkTower.y > tower.y
                    else:
                        inFront = checkTower.y< tower.y
                    if inFront:
                        checkLeftRight = np.sign(checkTower.x-tower.x) == np.sign(destX-tower.x)
                        if difY<=difYGlobal and checkLeftRight:
                            return True
        return False

    def isThereATowerInFront(self,tower,destY,destX):
        if destX != tower.x:
            return True
        towardBot = True
        if destY<tower.y:
            towardBot = False
        for checkTower in self.p0Towers+self.p1Towers:
            if checkTower.x != tower.x or checkTower.y != tower.y:
                if checkTower.x == tower.x:
                    if towardBot and checkTower.y > tower.y and checkTower.y < destY:
                        return True
                    elif not towardBot and checkTower.y<tower.y and checkTower.y>destY:
                        return True
        return False

    def NoRetreat(self,tower,destY):
        if tower.indexPlayer == 0 and destY>tower.y:
            return True
        elif tower.indexPlayer == 1 and destY<tower.y:
            return True
        else:
            return False

    def squareIsEmpty(self,x,y):
        for tower in self.p0Towers+self.p1Towers:
            if tower.x == x and tower.y == y:
                return False
        return True

    def selectLastTurnColorTower(self):
        if self.playerTurn == 0:
            checkTowers = self.p0Towers
        else:
            checkTowers = self.p1Towers
        for tower in checkTowers:
            if tower.colorKey == self.lastTurnColor:
                self.selectedTower = tower
        if not self.isAbleToMove(self.selectedTower):
            self.changeTurn()
            self.lastTurnColor = self.board[self.selectedTower.y][self.selectedTower.x]
            self.selectLastTurnColorTower()

    def playerSelectDestination(self,pos):
        x = math.trunc(pos[0] / self.squareSizeX)
        y = math.trunc(pos[1] / self.squareSizeY)
        if self.squareIsEmpty(x,y):
            if not self.isThereATowerInFront(self.selectedTower,y,x) or not self.isTHereATowerInDiagonale(self.selectedTower,y,x):
                if self.NoRetreat(self.selectedTower,y):
                    self.selectedTower.y = y
                    self.selectedTower.x = x
                    self.lastTurnColor = self.board[y][x]
                    self.selectedTower = None
                    self.changeTurn()
                    if self.currentTurn > 0:
                        self.selectLastTurnColorTower()
                    else:
                        self.lastTurnColor = self.board[self.selectedTower.y][self.selectedTower.x]


import cv2
import numpy as np
import onnxruntime
from swap_mukham.utils.device import OnnxInferenceSession

class Occluder(OnnxInferenceSession):
    def __init__(self, *args, **kwargs):
        super().__init__(**kwargs)

        inputs = self.session.get_inputs()
        self.input_name = inputs[0].name

        if "in_face" in self.input_name:
            # xseg occluder
            self.crop_size = inputs[0].shape[1:3]
            self.pre_order = (0, 1, 2, 3)
            self.post_order = (0, 1, 2)
        else:
            # generic occluder
            self.crop_size = (256, 256)
            self.pre_order = (0, 3, 1, 2)
            self.post_order = (1, 2, 0)

    def predict(self, img):
        img_size = img.shape[:2]
        img = cv2.resize(img, self.crop_size)
        img = np.expand_dims(img, axis=0).astype("float32") / 255
        img = img.transpose(*self.pre_order)

        mask = self.session.run(None, {self.input_name: img})[0][0]
        mask = mask.transpose(*self.post_order)
        mask = mask.clip(0, 1).astype("float32")
        mask = cv2.resize(mask, img_size)

        return mask

    def delete_session(self):
        if hasattr(self, "session"):
            del self.session


from decimal import Decimal
from typing import List, Dict, Callable, Any, Union

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

from abcckpt import ckptParser
from abcckpt import fast_vrf
from abccore.DAG import Wallet
from abccore.checkpoint_service import CheckpointService
from abccore.prefix_tree import Tree
from abcnet import transcriber
from abcnet.services import BaseApp
from abcnet.structures import MsgType, Message
from abcckpt.ckptItems import CkptItemType, Priority, CkptHash, CkptData, ValidatorVote, MajorityVotes
from abcckpt.ckptParser import CkptItemsParser
from abcckpt.ckpt_creation_state import CkptCreationState
from abcckpt.content_handler import ContentHandler
from abcckpt.hash_handler import HashHandler
from abcckpt.pre_checkpoint import PreCheckpoint, AgentService, PreCkptItemProcessor
from abcckpt.prio_cr_handler import PriorityCrHandler
from abcckpt.prio_handler import PriorityHandler
from abcckpt.proposal_cr_handler import ProposalCrHandler
from abcckpt.stab_abc_consens import StabVotingHandler
from abcckpt.vote_cr_handler import VoteCrHandler

def inject_ckpt_items_into_oracle():
    from abcnet import settings
    type_oracle = settings.NetStatSettings.item_type_oracle()
    type_oracle.update({
        CkptItemType.PRIORITY: "PRIO",
        CkptItemType.CKPT_HASH: "CKPT_HASH",
        CkptItemType.VALVOTE: "VOTE",
        CkptItemType.CKPT_DATA: "CKPT_DATA",
        CkptItemType.MAJVOTES: "MAJORITY_VOTES"
    })
    packer_oracle = settings.NetStatSettings.item_packer_oracle()

    def pack_state(state: CkptCreationState) -> Dict:
        return {
            "last_common_string": state.last_common_string.hex(),
            "round": state.round,
            "step_status": state.step_status.name,
            "chosen_validator": state.chosen_validator.hex() if state.chosen_validator is not None else 'Not set',
            "content_hash": state.content_hash.hex() if state.content_hash is not None else 'Not set',
            "current_common_str": state.current_common_str.hex()
        }

    def pack_priority(p: Priority) -> Dict:
        return {
            "state": pack_state(p.state),
            "pub_k": p.pub_k.hex(),
            "stake": str(p.stake),
            "proof": p.proof.hex()[:15] + "...",
            "votes": p.votes
        }

    def pack_hash(p: CkptHash) -> Dict:
        return {
            "state": pack_state(p.state),
            "checkpoint hash": p.ckpt_hash.hex(),
        }

    def pack_content(p: CkptData) -> Dict:
        return {
            "state": pack_state(p.state),
            "checkpoint hash": p.ckpt_hash.hex(),
            # "total_stake": str(p.ckpt_total_stake),
            # "total coins": p.ckpt_total_coins,
        }

    def pack_vote(p: ValidatorVote) -> Dict:
        return {
            "state": pack_state(p.state),
            "voted item type": p.voted_item_type,
            "voted item id": p.voted_item_id,
        }

    def pack_majority_vote(p: MajorityVotes) -> Dict:
        return {
            "state": pack_state(p.state),
            "voted item id": p.voted_item_qualifier
            # number of votes?
        }

    parser = CkptItemsParser()
    packer_oracle.update({
        CkptItemType.PRIORITY: lambda b:
        pack_priority(parser.decode_item_bytes(CkptItemType.PRIORITY, b)),
    })
    packer_oracle.update({
        CkptItemType.CKPT_HASH: lambda b:
        pack_hash(parser.decode_item_bytes(CkptItemType.CKPT_HASH, b)),
    })
    packer_oracle.update({
        CkptItemType.CKPT_DATA: lambda b:
        pack_content(parser.decode_item_bytes(CkptItemType.CKPT_DATA, b)),
    })
    packer_oracle.update({
        CkptItemType.MAJVOTES: lambda b:
        pack_majority_vote(parser.decode_item_bytes(CkptItemType.MAJVOTES, b)),
    })
    packer_oracle.update({
        CkptItemType.VALVOTE: lambda b:
        pack_vote(parser.decode_item_bytes(CkptItemType.VALVOTE, b)),
    })

def pseudo_pc(common_str: bytes = None) -> PreCheckpoint:
    if common_str is None:
        common_str = b'test_common_str'
    return PreCheckpoint(CkptCreationState(common_str))

def add_prio_app(ba: BaseApp, ckpt_service: CheckpointService, pc: PreCheckpoint) \
        -> PriorityHandler:
    if pc is None:
        pc = pseudo_pc()
    ph = PriorityHandler(pre_ckpt=pc, ckpt_service=ckpt_service)
    ba.register_app_layer("prio_handler", ph)
    return ph

def add_hash_app(ba: BaseApp, pc: PreCheckpoint) \
        -> HashHandler:
    if pc is None:
        pc = pseudo_pc()
    ph = HashHandler(pre_ckpt=pc)
    ba.register_app_layer("hash_handler", ph)
    return ph

def add_stab_vote_handler_app(ba: BaseApp, ckpt_service: CheckpointService, pc: PreCheckpoint,
                              processors: List[PreCkptItemProcessor] = None) \
        -> StabVotingHandler:
    svh = StabVotingHandler(pc, ckpt_service)
    if processors is not None:
        svh.set_handlers(*processors)
    ba.register_app_layer("stab_vote_handler", svh)
    return svh

def ckpt_protocol_app(ba: BaseApp, ckpt_service: CheckpointService, agent: AgentService, pc: PreCheckpoint):
    ph = add_prio_app(ba, ckpt_service, pc)
    hh = add_hash_app(ba, pc)
    ch = ContentHandler(pc, agent, ckpt_service)
    ba.register_app_layer("content_handler", ch)

    svh = add_stab_vote_handler_app(ba, ckpt_service, pc)

    priority_creator = PriorityCrHandler(pc, ckpt_service, agent)
    ba.register_app_layer("priority_creator", priority_creator)
    vote_creator = VoteCrHandler(pc, agent)
    ba.register_app_layer("vote_creator", vote_creator)
    proposal_creator = ProposalCrHandler(pc, agent, ckpt_service)
    ba.register_app_layer("proposal_creator", proposal_creator)

    ph.set_handlers(vote_creator)
    hh.set_handlers(vote_creator)
    ch.set_handlers(vote_creator)
    svh.set_handlers(ph, hh, ch)

    priority_creator.set_handlers(ph)
    vote_creator.set_handlers(svh)
    proposal_creator.set_handler(hh, ch)

class CheckpointServiceTestCase(CheckpointService):
    private_keys: List[Ed25519PrivateKey] = []

    pub_keys: List[bytes] = []

    stake_distribution: Dict[bytes, Decimal] = {}

    owner1: Ed25519PrivateKey
    owner1_pub_key: bytes

    @classmethod
    def stake_sum(cls) -> Decimal:
        return sum(cls.stake_distribution.values())

    @classmethod
    def delegated_stake(cls, pb_key) -> Decimal:
        return cls.stake_distribution.get(pb_key, Decimal(0.0))

    @classmethod
    def owned_wallets(cls, pb_key) -> List[Wallet]:
        raise Exception("This method should not be used in the checkpoint creation tests.")

class CheckpointCase1(CheckpointServiceTestCase):
    validator_count = 10

    private_keys: List[Ed25519PrivateKey] = []

    pub_keys: List[bytes] = []

    stake_distribution: Dict[bytes, Decimal] = {}

    owner1: Ed25519PrivateKey

    owner1_pub_key: bytes

    @classmethod
    def init_case(cls):
        for i in range(cls.validator_count):
            private_key = fast_vrf.gen_key(i)
            pub_key = fast_vrf.encode_pub_key(private_key.public_key())
            cls.private_keys.append(private_key)
            cls.pub_keys.append(fast_vrf.encode_pub_key(private_key.public_key()))
            cls.stake_distribution[pub_key] = Decimal(100.0)

            del private_key
            del pub_key

        cls.owner1: Ed25519PrivateKey = fast_vrf.gen_key("Owner_1")
        cls.owner1_pub_key: bytes = fast_vrf.encode_pub_key(cls.owner1.public_key())
        cls.stake_distribution[cls.owner1_pub_key] = Decimal(50.0)

    @classmethod
    def get_ckpt_id(cls):
        return b'test_common_str'

    @classmethod
    def get_height(cls):
        return 0

    @classmethod
    def get_ckpt_origin(self) -> bytes:
        return b'test_common_str'

CheckpointCase1.init_case()
CheckpointCase1: CheckpointService

class AgentSerivceMock(AgentService):

    def __init__(self, pv_keys: List[Ed25519PrivateKey], dag):
        self.pv_keys = pv_keys
        self.dag = dag

    def get_keypairs(self) -> List[Ed25519PrivateKey]:
        return self.pv_keys

    def get_DAG(self) -> Tree:
        return self.dag

parser = ckptParser.CkptItemsParser()

def ckptitems_matcher(filter_msg_type: MsgType, filter_item_type: CkptItemType):
    assert filter_msg_type in [MsgType.items_content, MsgType.items_request,
                               MsgType.items_checklist, MsgType.items_notfound]

    def annotation(items_matcher: Callable[[List[Union[str, Any]]], Any]):
        def new_matcher(msg: Message):
            m_type = transcriber.parse_message_type(msg)
            if m_type != filter_msg_type:
                return False

            if filter_msg_type == MsgType.items_content:
                items = transcriber.parse_item_contents(msg)
            else:
                items = transcriber.parse_item_qualifier(msg)
            filtered_items = list(map(lambda i: i[1], filter(lambda i: i[0] == filter_item_type, items)))
            if not filtered_items:
                return False
            if filter_msg_type == MsgType.items_content:
                filtered_items = [parser.decode_item_bytes(filter_item_type, i) for i in filtered_items]

            return items_matcher(filtered_items)

        return new_matcher

    return annotation


from scipy.stats import norm
import numpy as np
from typing import List

"""
Génération de deux v.a normales à partir de deux v.a indépendantes uniformes.
Utilisation de la méthode de Box et Müller.
Obtention d'une variable centrée réduite.
"""
def generate_normale_law(uniform_values1: List[float], uniform_values2: List[float]):
    U = uniform_values1
    V = uniform_values2

    theta = 2 * np.pi * V
    R = -2 * np.log(U)

    X = np.sqrt(R) * np.cos(theta)
    Y = np.sqrt(R) * np.sin(theta)

    return X, Y

"""
Probabilité([a,b]) d'une loi normale.
"""
def normale_probability(mu: float, sigma: float, a: float, b: float) -> float:
    return norm.cdf(b, mu, sigma) - norm.cdf(a, mu, sigma)


from django.urls import path,re_path,include
from . import views

urlpatterns=[
       path('',views.home,name='home'),
       path('forms/',views.forms,name='forms'),
       path('announcements/',views.AnnouncementsListView.as_view(),name='announcement_list'),
       path('contact-info/',views.contact,name='contact'),
       path('townhall-and-sc-meetings/',views.MeetingListView.as_view(),name='meeting_list'),
       path('grievances/',views.grievances,name='grievances'),
       path('student-activities/',views.club,name='club'),
       path('documents/',views.DocumentsListView.as_view(),name='documents_list'),
]


import graphene
from graphene_django.types import DjangoObjectType
from .models import Service

class ServiceType(DjangoObjectType):
    class Meta:
        model = Service

class Query(graphene.ObjectType):
    services = graphene.List(ServiceType)

    def resolve_services(self, info, **kwargs):
        return Service.objects.all()


from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
validator_fn = [
    ##RegexValidator(r'[#]s+', "Hashtag #.....(любой символ)"),
    RegexValidator(r'@[w.]*','Login @...(0-9,a-z,_)')

]
validator_fn2 = [
    RegexValidator(r'[#]s+', "Hashtag #.....(любой символ)"),

]
def valid(value):
    err = None
    if (value[0] == "@"):
        for validator in validator_fn:
            try:
                validator(value)
            # Valid value, return it
                return value
            except ValidationError:
                return "Введенные данные: " + value + ". Неправильный формат для логина, логин: @....( вместо точек любая буква английского алфавита, цифра или нижнее подчеркивание)"

    if (value[0] == "#"):
        for validator in validator_fn:
            try:
                validator(value)
            # Valid value, return it
                return value
            except ValidationError:
                return "Введенные данные: " + value + ". Неправильный формат для хештега, хештег: #....( вместо точек любой символ кроме пробела)"
    else:
        return


# programmers - Lv2 - 다리를 지나는 트럭 (2020-09-15)
# https://programmers.co.kr/learn/courses/30/lessons/42583

import sys
sys.stdin = open("programmers/[Lv2]다리를지나는트럭.txt",'r')
from collections import deque

def solution(bridge_length, weight, truck_weights):
    drive_weight = deque() # 다리에 운전중인 트럭들의 무게
    drive_len = deque() # 다리에 운전중인 트럭들의 위치

    drive_weight.append(truck_weights.pop(0))
    drive_len.append(1)

    answer = 1 # 총 시간

    while(drive_weight):

        if drive_len[0] >= bridge_length :  # 맨 앞의 트럭이 다리를 건넜을때,
            drive_len.popleft()
            drive_weight.popleft()

        for i in range(len(drive_len)): # 트럭의 위치를 한칸씩 증가
            drive_len[i] += 1

        if len(truck_weights) > 0 : # 대기중인 트럭 있는지 여부
            if truck_weights[0] + sum(drive_weight) <= weight: # 대기중 첫 트럭과 운행중 트럭의 합이 weight가 걸리는지
                drive_len.append(1)
                drive_weight.append(truck_weights.pop(0))

        answer += 1

    return answer

T = int(input())
for _ in range(T):
    bridge_length, weight = map(int, input().split())
    truck_weights = list(map(int, input().split()))

    print(solution(bridge_length, weight, truck_weights))

[Lv2]다리를지나는트럭.py


import torch

import numpy as np

from .basemodel_torch import BaseModelTorch
from .deepfm_lib.models.deepfm import DeepFM as DeepFMModel
from .deepfm_lib.inputs import SparseFeat, DenseFeat

'''
    DeepFM: A Factorization-Machine based Neural Network for CTR Prediction
     (https://www.ijcai.org/proceedings/2017/0239.pdf)

    Code adapted from: https://github.com/shenweichen/DeepCTR-Torch
'''

class DeepFM(BaseModelTorch):

    def __init__(self, params, args):
        super().__init__(params, args)

        if args.objective == "classification":
            print("DeepFM not yet implemented for classification")
            import sys
            sys.exit()

        if args.cat_idx:
            dense_features = list(set(range(args.num_features)) - set(args.cat_idx))
            fixlen_feature_columns = [SparseFeat(str(feat), args.cat_dims[idx])
                                      for idx, feat in enumerate(args.cat_idx)] + \
                                     [DenseFeat(str(feat), 1, ) for feat in dense_features]

        else:
            # Add dummy sparse feature, otherwise it will crash...
            fixlen_feature_columns = [SparseFeat("dummy", 1)] + \
                                     [DenseFeat(str(feat), 1, ) for feat in range(args.num_features)]

        self.model = DeepFMModel(linear_feature_columns=fixlen_feature_columns,
                                 dnn_feature_columns=fixlen_feature_columns,
                                 task=args.objective, device=self.device, dnn_dropout=self.params["dnn_dropout"],
                                 gpus=self.gpus)

    def fit(self, X, y, X_val=None, y_val=None):
        X = np.array(X, dtype=np.float)
        X_dict = {str(name): X[:, name] for name in range(self.args.num_features)}

        X_val = np.array(X_val, dtype=np.float)
        X_val_dict = {str(name): X_val[:, name] for name in range(self.args.num_features)}

        if self.args.objective == "binary":
            loss = "binary_crossentropy"
            metric = "binary_crossentropy"
            labels = [0, 1]
        elif self.args.objective == "regression":
            loss = "mse"
            metric = "mse"
            labels = None

        self.model.compile(optimizer=torch.optim.AdamW(self.model.parameters()),
                           loss=loss, metrics=[metric])

        # Adding dummy spare feature
        if not self.args.cat_idx:
            X_dict["dummy"] = np.zeros(X.shape[0])
            X_val_dict["dummy"] = np.zeros(X_val.shape[0])

        loss_history, val_loss_history = self.model.fit(X_dict, y, batch_size=self.args.batch_size,
                                                        epochs=self.args.epochs,
                                                        validation_data=(X_val_dict, y_val), labels=labels,
                                                        early_stopping=True,
                                                        patience=self.args.early_stopping_rounds)
        return loss_history, val_loss_history

    def predict_helper(self, X):
        X = np.array(X, dtype=np.float)
        X_dict = {str(name): X[:, name] for name in range(self.args.num_features)}

        # Adding dummy spare feature
        if not self.args.cat_idx:
            X_dict["dummy"] = np.zeros(X.shape[0])
        return self.model.predict(X_dict, batch_size=self.args.batch_size)

    @classmethod
    def define_trial_parameters(cls, trial, args):
        params = {
            "dnn_dropout": trial.suggest_float("dnn_dropout", 0, 0.9),
        }
        return params

        # dnn_dropout, l2_reg_linear, l2_reg_embedding, l2_reg_dnn, dnn_hidden_units?


from collections import defaultdict

import numpy as np

from .base.stage_summarizer_base import StageSummarizerBase

class LogregSummarizer(StageSummarizerBase):
    def summarize(self):
        # group values
        grouped = defaultdict(list)
        for key, value in self.all_log_entries.items():
            if "accuracy1" not in key:
                continue
            assert len(value) == 1
            key = key.replace("_split1", "").replace("_split2", "").replace("_split3", "")
            grouped[key].append(value[0])
        # stats
        for key, values in grouped.items():
            mean = float(np.mean(values))
            std = float(np.std(values))
            self.logger.info(f"{key}: mean={mean:.4f} std={std:.4f}")
            self.summary_provider[f"{key}/mean"] = mean
            self.summary_provider[f"{key}/std"] = std


import numpy as np
from scipy.stats import pearsonr

def zeroElimination(lst):
    for i in range(len(lst)):
        if list[i] == 0:
            list[i] = 0.0001

def regressionValues(strategyDict, columns):
    listOfReqVal = ['dBTC ', 'd24BTC ', 'dMarket ', 'dM24 ', 'bvsv ', 'dBTC5m ', 'Pump1H ', 'Dump1H ', 'd24h ', 'd3h ',
                    'd1h ', 'd15m ', 'd5m ', 'd1m ', 'dBTC1m ', 'Vd1m ']
    tempListOFprofit = []
    tempListSecondParamValForStrat = []
    tempListSecondParamValForEl = []
    tempCorrValList = []
    profitDict = {}
    secondparamDict = {}
    corrDict = {}
    el = 0
    # i = 0
    for stratName in strategyDict[1]:
        # this for is needed to iterate over every strategy
        # in it we calculate regression for this strategies
        while el < strategyDict[1][stratName].__len__():  # el is number of trades in strategy
            tempListOFprofit.append(int(strategyDict[1][stratName][el][1][6]))
            # we add the value of profit of every
            # trade (num of trade is el) of the strategy we are currently on
            # (strategy name iterates on first for by stratName value)
            el = el + 1
        # i = i + 1
        # service check of list
        # print(' Num of str: ' + str(i) + ' | Num of elem in list: ' + str(el)
        #      + ' | list: ' + str(listOfprofit)
        # regrassion calculations
        el = 0

        for val in listOfReqVal:
            while el < strategyDict[1][stratName].__len__():
                index = columns.index(val)
                # тут по порядку для каждой сделки все данные listOfReqVal
                tempListSecondParamValForEl.append(strategyDict[1][stratName][el][1][int(index)])
                el = el + 1
            el = 0
            tempListSecondParamValForStrat.append(tempListSecondParamValForEl)
            tempListSecondParamValForEl = []

        secondparamDict[stratName] = tempListSecondParamValForStrat  # list with all the market data from log file
        profitDict[stratName] = tempListOFprofit  # data for regression calculation
        tempListSecondParamValForStrat = []
        tempListOFprofit = []

    for stratName in strategyDict[1]:
        Y = np.array(profitDict[stratName])  # Y -  dependent variables whose values are to be predicted
        for el in listOfReqVal:  # параметры рынка
            X = np.array(secondparamDict[stratName][listOfReqVal.index(el)])
            # X : numpy array or sparse matrix of shape [n_samples,n_features]
            if 0 in X:
                zeroElimination(X)
            if X.__len__() < 1:
                tmcor = 'Not enough data'
                tempCorrValList.append(tmcor)
            elif X.__len__() > 1:
                tmcor = pearsonr(X, Y)
                tempCorrValList.append(tmcor[0])
            tmcor = []
            # print(tmcor[1])
        corrDict[stratName] = tempCorrValList
        tempCorrValList = []

    # for stratName in strategyDict[1]:
    #     print('\nCorrelations for strategy ' + str(stratName))
    #     if corrDict[stratName].__len__() == 0:
    #         print('Not Enough data')
    #     else:
    #         for el in listOfReqVal:
    #             print('parameter: ' + str(el) + ' is ' + '{0:g}'.format(corrDict[stratName][listOfReqVal.index(el)]))

    # print('\n')

    return corrDict, listOfReqVal

# TODO
#   X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2, random_state=0)
#                 # X_train = X_train.reshape(-1, 1)
#                 # y_train = y_train.reshape(-1, 1)
#                 # regressor = LinearRegression()
#                 # regressor.fit(X_train, y_train)
#             #y_pred = regressor.predict(X_test)
#             #df_pred = pd.DataFrame({'Actual': y_test, 'Predicted': y_pred})
#             #print(df_pred)
#             # Y_pred = linearRegression.predict(X)  # make predictions\

# TODO How to print graph
#     # plt.scatter(X, Y)
#     # plt.plot(X, Y_pred, color='red')
#     # plt.title('Strategy ' + str(stratName) + ', regression for ' + str(el))
#     # plt.xlabel('profit')
#     # plt.ylabel(el)
#     # plt.show()
#     # print(profitDict[stratName]) np.array(
#     # print(secondparamDict[stratName][el])


import abc
from typing import Any, Mapping, Optional, TYPE_CHECKING

from marshmallow import Schema, fields as fs, ValidationError

from templates import Interaction
from utils import EmbedFactory

if TYPE_CHECKING:
    from templates.views.dnd_resource_menus.framework import ResourceMenu

class UnionField(fs.Field):
    def __init__(self, fields: list[fs.Field] = None, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        if fields:
            self.fields = fields
        else:
            raise AttributeError('No types provided for UnionField')

    def _deserialize(
        self,
        value: Any,
        attr: Optional[str],
        data: Optional[Mapping[str, Any]],
        **kwargs,
    ):
        errors = []
        for field in self.fields:
            try:
                value = field._deserialize(value, attr, data, **kwargs)
                return value
            except ValidationError as e:
                errors.append(e)
                continue
        raise ValidationError(errors)

    def _serialize(self, value: Any, attr: str, obj: Any, **kwargs):
        for field in self.fields:
            try:
                value = field._serialize(value, attr, obj, **kwargs)
                return value
            except Exception:
                continue

        raise AttributeError(f"Could not serialize value for {self.__repr__()}")

    def __repr__(self) -> str:
        return f"<UnionField fields=[{', '.join(repr(field) for field in self.fields)}]>"

class APIModel(abc.ABC):
    schema: Schema

BASE_URL = "https://www.dnd5eapi.co"

class ResourceModel(APIModel, abc.ABC):
    @property
    def full_url(self) -> str:
        if hasattr(self, 'url'):
            return f"{BASE_URL}{self.url}"
        raise NotImplementedError

    def to_menu(
            self,
            interaction: Interaction,
            factory: EmbedFactory,
            ephemeral: bool = False
    ) -> 'ResourceMenu':
        raise NotImplementedError


from types import MethodType
from unittest import result

import numpy as np
import cv2
from PySide2.QtWidgets import QMainWindow,QLabel
from PySide2.QtGui import QImage,QPixmap
from PySide2.QtCore import Qt,Signal,QEvent
from PIL.ImageQt import ImageQt
from pdf2image import convert_from_path

from .utils import loadUi
from .elaboration import ElaborationManager,ElaborationHandler

def dragEnterEvent(self, event):

	if event.mimeData().hasUrls:
		event.accept()
	else:
		event.ignore()

def dragMoveEvent(self, event):

	if event.mimeData().hasUrls:
		event.accept()
	else:
		event.ignore()

def dropEvent(self,event):

	if event.mimeData().hasUrls:
		event.setDropAction(Qt.CopyAction)
		filepath = event.mimeData().urls()[0].toLocalFile()
		self.set_image(filepath)
		event.accept()
	else:
		event.ignore()

def set_image(self,filepath):
	self.image = None
	self.image = convert_from_path(filepath)[0]
	self.npimg = np.array(self.image)
	image = ImageQt(self.image)
	qpimage = QPixmap.fromImage(image)
	qpimage = qpimage.scaled(556, 800,Qt.KeepAspectRatio)
	self.setPixmap(qpimage)
	self.setAlignment(Qt.AlignCenter)

class Home(QMainWindow):
	def __init__(self):
		super(Home,self).__init__()
		loadUi("./uiFiles\home.ui",self)
		self.image_label.installEventFilter(self)
		self._add_method_to_label_class(self.image_label)
		self.create_dictionary()
		self.elabManager = ElaborationManager()
		self.elabHandler = ElaborationHandler(self.elabManager.results)
		self.elabHandler.new_results.connect(self.plot_results)
		self.readButton.clicked.connect(self.read_image)
		self.elabHandler.start()

	def create_dictionary(self):
		self.image_labels = {}
		self.image_labels['nome'] = self.image_name
		self.image_labels['data'] = self.image_data
		self.image_labels['targa'] = self.image_targa
		self.image_labels['cf'] = self.image_cf
		self.image_labels['via'] = self.image_via

		self.text_labels = {}
		self.text_labels['nome'] = self.text_name
		self.text_labels['data'] = self.text_data
		self.text_labels['targa'] = self.text_targa
		self.text_labels['cf'] = self.text_cf
		self.text_labels['via'] = self.text_via

	def _add_method_to_label_class(self,label):
		label.setAcceptDrops(True)
		label.image = None
		label.npimg = None
		label.dragEnterEvent = MethodType(dragEnterEvent,label)
		label.dragMoveEvent = MethodType(dragMoveEvent,label)
		label.dropEvent = MethodType(dropEvent,label)
		label.set_image = MethodType(set_image,label)

	def plot_results(self,results):

		for key,(image,text) in results.items():
			if key == "nome":
				self.image_labels[key].setPixmap(self.imgtoqpixmap(image))
				text = text.tolist()
				text = " ".join(text)
				self.text_labels[key].setText(text)
			elif key == "via":
				self.image_labels[key].setPixmap(self.imgtoqpixmap(image))
				text = text.tolist()
				text = " ".join(text)
				self.text_labels[key].setText(text.replace("()",""))
			else:
				self.image_labels[key].setPixmap(self.imgtoqpixmap(image))
				self.text_labels[key].setText(text.replace(')',""))

	def imgtoqpixmap(self,image):
		h,w,c = image.shape
		bytesPerLine = c*w
		qimage = QImage(image,w,h,bytesPerLine,QImage.Format.Format_RGB888).scaled(250,70)
		qpimage = QPixmap.fromImage(qimage)
		return qpimage

	def read_image(self):
		if self.image_label.npimg is not None and self.elabManager is not None:
			self.elabManager.imagequeue.put(self.image_label.npimg)

	def eventFilter(self, o, e):
		if e.type() == QEvent.DragEnter: #remember to accept the enter event
			self.image_label.dragEnterEvent(e)
		if e.type() == QEvent.Drop:
			self.image_label.dropEvent(e)
			# ...
			return True
		return False #remember to return false for other event types