# test CurlypivSetup
"""
Notes about program
"""

# 1.0 import modules
import numpy as np

# plotting
import matplotlib.pyplot as plt
import matplotlib
from matplotlib import colors
import matplotlib.image as mgimg
from matplotlib import animation
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
matplotlib.rcParams['figure.figsize'] = (7, 6)
import matplotlib.font_manager as fm
fontprops = fm.FontProperties(size=16, weight='bold')
font = {'family' : 'sans-serif',
        'weight' : 'light',
        'size'   : 16}
matplotlib.rc('font', **font)
matplotlib.rcParams['font.sans-serif'] = ['Helvetica']

# OpenPIV
# ----- imports for OpenPIV -----
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/Users/mackenzie/PythonProjects/openpiv')
import openpiv.piv
from openpiv import windef
from openpiv.windef import Settings

# 2.0 define class

class CurlypivPIVSetup(object):

    def __init__(self, name, save_text, save_plot, testCollection, testSetup,
                 win1=128, win2=64, show_plot=False, save_plot_path=None, save_text_path=None, vectors_on_image=True,
                 calculate_zeta=False, replace_Nans_with_zeros=True, save_u_mean_plot=False,
                 u_min=-40, u_max=40, v_min=-2.5, v_max=2.5):
        """
        Notes
        """

        # setup
        self._name = name
        self.save_text = save_text
        self.save_plot = save_plot
        self.save_u_mean_plot = save_u_mean_plot
        self.save_text_path = save_text_path
        self.save_plot_path = save_plot_path
        self.show_plot = show_plot
        self.calculate_zeta = calculate_zeta

        # OpenPIV
        self.settings = Settings()

        # plotting
        self.vectors_on_image = vectors_on_image
        self.settings.scale_plot = 1
        self.colorMap = 'plasma'
        self.colorNorm = colors.Normalize(vmin=0, vmax=75)
        self.alpha = 0.65
        self.scalebar_microns = int(2500 / testSetup.optics.microscope.objective.magnification) # units are microns
        self.dpi = 200

        # camera
        self.img_acq = testSetup.optics.microscope.ccd.img_acq_rate
        self.dtf = 1/self.img_acq
        self.pixels_to_microns = testSetup.optics.microscope.objective.pixel_to_micron
        self.pix_per_um = 1/self.pixels_to_microns

        # experimental
        self.E_max = 10e3
        self.particle_diameter = testSetup.optics.fluorescent_particles.diameter
        self.est_zeta = testSetup.chip.channel.material_bottom_wall_surface.zeta

        # scientific
        self.epsr = 80
        self.eps = self.epsr*8.854e-12
        self.mu = testSetup.chip.channel.material_fluid.viscosity

        # outputs
        self.est_u_eof = self.eps*self.est_zeta*self.E_max/self.mu*1e6
        self.char_u_eof = -self.est_u_eof*self.pix_per_um*self.dtf
        self.char_u = int(np.round(self.char_u_eof))

        # more OpenPIV
        self.settings.correlation_method = 'linear'
        self.settings.normalized_correlation = True
        self.settings.deformation_method = 'symmetric'  # 'symmetric' or 'second image'
        self.settings.windowsizes = (win1, win2)  # sizex//4, sizex//8 suggestion is these are power of 2 of each other
        self.settings.overlap = (win1//2, win2//2)  # should be 50%-75% of window size (Raffel)
        self.settings.num_iterations = len(self.settings.windowsizes)
        self.settings.subpixel_method = 'gaussian'  # subpixel interpolation: 'gaussian','centroid','parabolic'
        self.settings.interpolation_order = 3  # interpolation order for the window deformation (suggested: 3-5)
        self.settings.scaling_factor = self.pix_per_um  # scaling factor pixel/meter
        self.settings.dt = self.dtf  # time between to frames (in seconds)
        self.settings.ROI = ('full')

        # snr parameters
        self.mask_first_pass = True  # Mask first pass
        self.mask_multi_pass = True
        #self.settings.extract_sig2noise = True  # Compute SNR for last pass / if False: SNR set to NaN in output txt.
        self.settings.image_mask = True  # Do image masking
        self.settings.sig2noise_method = 'peak2peak'  # Method to compute SNR: 'peak2peak' or 'peak2mean'
        self.settings.sig2noise_mask = 3  # (2 - 5) exclusion distance between highest peak and second highest peak in correlation map
        # min/max velocity vectors for validation
        self.u_min = u_min # microns / second
        self.u_max = u_max
        self.v_min = v_min # microns / second
        self.v_max = v_max
        self.settings.MinMax_U_disp = (self.u_min * self.pix_per_um * self.dtf, self.u_max * self.pix_per_um * self.dtf)  # filter u (units: pixels/frame)
        self.settings.MinMax_V_disp = (self.v_min * self.pix_per_um * self.dtf, self.v_max * self.pix_per_um * self.dtf)  # filter v (units: pixels/frame)
        # vector validation
        self.settings.validation_first_pass = True  # Vector validation of first pass
        self.u_uncertainty = 10  # if std(u)*2 < uncertainty: don't apply global std threshold
        self.v_uncertainty = 10  # if std(v)*2 < uncertainty: don't apply global std threshold
        self.settings.std_threshold = 2.75  # global std validation threshold: global mean +/- stdev * std_threshold
        self.settings.median_threshold = 2.75  # median validation threshold
        self.settings.median_size = 2  # defines the size of the local median kernel
        self.settings.sig2noise_validate = True  # Enables validation by SNR ratio
        self.settings.sig2noise_threshold = 1.2  # [1.2-1.5] Sets snr threshold for removing vectors (R. D. Keane and R. J. Adrian, Measurement Science & Technology, 1990)
        # outlier replacement and smoothing
        self.settings.replace_vectors = True  # Outlier replacement for last pass
        self.replace_Nans_with_zeros = replace_Nans_with_zeros # Outlier replacement where all Nans = 0
        self.settings.smoothn = False  # Enables Garcia smoothing function of velocity fields
        self.settings.smoothn_p = [0.01]  # [0.5] Smoothing parameter or auto-calculated using generalized cross-validation (GCV) method
        self.settings.filter_method = 'distance'  # Replace outlier vector method: localmean [square] or disk (unweighted circle), distance (weighted circle)
        self.settings.max_filter_iteration = 3  # maximum iterations performed to replace the outliers (max 10)
        self.settings.filter_kernel_size = 2 # kernel size for replacing outlier vectors (default

        self.settings._freeze()

        # print PIV settings
        print('Min/Max U-displacement: ', self.settings.MinMax_U_disp, ' (pixels/frame)')
        print('Min/Max U-displacement: ', np.array([self.settings.MinMax_U_disp[0], self.settings.MinMax_U_disp[1]], dtype=int)*self.pixels_to_microns/self.dtf, ' (um/s)')

        print('Min/Max V-displacement: ', self.settings.MinMax_V_disp, ' (pixels/frame)')
        print('Min/Max V-displacement: ', np.array([self.settings.MinMax_V_disp[0],self.settings.MinMax_V_disp[1]], dtype=int)*self.pixels_to_microns/self.dtf, ' (um/s)')


import atexit
import os
import re
import shutil
import subprocess
import sys
import tempfile

def usage():
    sys.stderr.write('Usage: {} [-h] -d <directory> [-b <bitrate>]\n'.format(sys.argv[0]))
    sys.stderr.write('   -h display help\n')
    sys.stderr.write('   -b bitrate [32-320, default 192]\n')
    sys.stderr.write('   -d the target directory\n')
    sys.exit(1)

def get_track_file(path_prefix, track):
    return "%s/%02d %s.mp3" % (path_prefix, int(track['track']), sanitize(track['title']))

def sanitize(s):
    return s.replace("/", "-").replace(":", " -")

if __name__ == "__main__":
    target_dir = None
    bitrate = 192
    argc = 1

    while argc + 1 < len(sys.argv):
        if sys.argv[argc] == "-h":
            usage()
        elif sys.argv[argc] == "-d":
            target_dir = os.path.abspath(sys.argv[argc + 1])
            argc += 2
        elif sys.argv[argc] == "-b":
            bitrate = int(sys.argv[argc + 1])
            argc += 2
        else:
            break

    if target_dir is None or argc < len(sys.argv):
        usage()

    if not os.path.isdir(target_dir):
        sys.stderr.write("Directory '{}' doesn't exist\n".format(target_dir))
        exit(1)

    artist = None
    title = None
    year = None
    genre = None
    tracks = list()
    work_dir = tempfile.mkdtemp()

    os.chdir(work_dir)

    atexit.register(shutil.rmtree, work_dir)

    subprocess.check_call(["cdda2wav", "-alltracks", "-cddb", "1"])

    for file in os.listdir(work_dir):
        if file.endswith(".inf"):
            path = os.path.join(work_dir, file)

            with open(path, mode="r", encoding="iso8859-1") as f:
                track = dict()

                track['file'] = path[:-3] + "wav"

                for line in f:
                    parts = re.split(r"\s*=\s*", line.rstrip(), 1)

                    if parts[0] == "Albumperformer" and artist is None:
                        artist = parts[1].rstrip("'").lstrip("'")
                    elif parts[0] == "Albumtitle" and title is None:
                        title = parts[1].rstrip("'").lstrip("'")
                    elif parts[0] == "Tracknumber":
                        track['track'] = parts[1]
                    elif parts[0] == "Tracktitle":
                        track['title'] = parts[1].rstrip("'").lstrip("'")

                tracks.append(track)

    cddb_file = os.path.join(work_dir, "audio.cddb")

    if os.path.exists(cddb_file):
        with open(cddb_file, mode="r", encoding="iso8859-1") as f:
            for line in f:
                parts = re.split(r"\s*=\s*", line.rstrip(), 1)

                if parts[0] == "DYEAR":
                    year = parts[1]
                elif parts[0] == "DGENRE":
                    genre = parts[1]

    track_count = len(tracks)

    if track_count == 0:
        sys.stderr.write("No CDDB information available. Please process the files in {} manually\n".format(work_dir))
        atexit.unregister(shutil.rmtree)
        exit(1)

    path_prefix = os.path.join(target_dir, sanitize(artist), sanitize(title))

    os.makedirs(path_prefix, exist_ok=True)

    album_args = ["--ta", artist, "--tl", title, "--ty", year, "--tg", genre]

    for track in tracks:
        subprocess.check_call(["lame", "-b", str(bitrate), "-B", str(bitrate), "--tt", track['title'], "--tn",
                               "{}/{}".format(track['track'], track_count)] + album_args +
                              [track['file'], get_track_file(path_prefix, track)])


from __future__ import absolute_import
import logging
import math
#typing

import torch
import torch.nn.functional as F
#overrides

from allennlp.data import Vocabulary
from allennlp.models.model import Model
from allennlp.modules.token_embedders import Embedding
from allennlp.modules import FeedForward
from allennlp.modules import Seq2SeqEncoder, TimeDistributed, TextFieldEmbedder, SpanPruner
from allennlp.modules.span_extractors import SelfAttentiveSpanExtractor, EndpointSpanExtractor
from allennlp.nn import util, InitializerApplicator, RegularizerApplicator
from allennlp.training.metrics import MentionRecall, ConllCorefScores
try:
    from itertools import izip
except:
    izip = zip

logger = logging.getLogger(__name__)  # pylint: disable=invalid-name

class CoreferenceResolver(Model):
    u"""
    This ``Model`` implements the coreference resolution model described "End-to-end Neural
    Coreference Resolution"
    <https://www.semanticscholar.org/paper/End-to-end-Neural-Coreference-Resolution-Lee-He/3f2114893dc44eacac951f148fbff142ca200e83>
    by Lee et al., 2017.
    The basic outline of this model is to get an embedded representation of each span in the
    document. These span representations are scored and used to prune away spans that are unlikely
    to occur in a coreference cluster. For the remaining spans, the model decides which antecedent
    span (if any) they are coreferent with. The resulting coreference links, after applying
    transitivity, imply a clustering of the spans in the document.

    Parameters
    ----------
    vocab : ``Vocabulary``
    text_field_embedder : ``TextFieldEmbedder``
        Used to embed the ``text`` ``TextField`` we get as input to the model.
    context_layer : ``Seq2SeqEncoder``
        This layer incorporates contextual information for each word in the document.
    mention_feedforward : ``FeedForward``
        This feedforward network is applied to the span representations which is then scored
        by a linear layer.
    antecedent_feedforward: ``FeedForward``
        This feedforward network is applied to pairs of span representation, along with any
        pairwise features, which is then scored by a linear layer.
    feature_size: ``int``
        The embedding size for all the embedded features, such as distances or span widths.
    max_span_width: ``int``
        The maximum width of candidate spans.
    spans_per_word: float, required.
        A multiplier between zero and one which controls what percentage of candidate mention
        spans we retain with respect to the number of words in the document.
    max_antecedents: int, required.
        For each mention which survives the pruning stage, we consider this many antecedents.
    lexical_dropout: ``int``
        The probability of dropping out dimensions of the embedded text.
    initializer : ``InitializerApplicator``, optional (default=``InitializerApplicator()``)
        Used to initialize the model parameters.
    regularizer : ``RegularizerApplicator``, optional (default=``None``)
        If provided, will be used to calculate the regularization penalty during training.
    """
    def __init__(self,
                 vocab            ,
                 text_field_embedder                   ,
                 context_layer                ,
                 mention_feedforward             ,
                 antecedent_feedforward             ,
                 feature_size     ,
                 max_span_width     ,
                 spans_per_word       ,
                 max_antecedents     ,
                 lexical_dropout        = 0.2,
                 initializer                        = InitializerApplicator(),
                 regularizer                                  = None)        :
        super(CoreferenceResolver, self).__init__(vocab, regularizer)

        self._text_field_embedder = text_field_embedder
        self._context_layer = context_layer
        self._antecedent_feedforward = TimeDistributed(antecedent_feedforward)
        feedforward_scorer = torch.nn.Sequential(
                TimeDistributed(mention_feedforward),
                TimeDistributed(torch.nn.Linear(mention_feedforward.get_output_dim(), 1)))
        self._mention_pruner = SpanPruner(feedforward_scorer)
        self._antecedent_scorer = TimeDistributed(torch.nn.Linear(antecedent_feedforward.get_output_dim(), 1))

        self._endpoint_span_extractor = EndpointSpanExtractor(context_layer.get_output_dim(),
                                                              combination=u"x,y",
                                                              num_width_embeddings=max_span_width,
                                                              span_width_embedding_dim=feature_size,
                                                              bucket_widths=False)
        self._attentive_span_extractor = SelfAttentiveSpanExtractor(input_dim=text_field_embedder.get_output_dim())

        # 10 possible distance buckets.
        self._num_distance_buckets = 10
        self._distance_embedding = Embedding(self._num_distance_buckets, feature_size)

        self._max_span_width = max_span_width
        self._spans_per_word = spans_per_word
        self._max_antecedents = max_antecedents

        self._mention_recall = MentionRecall()
        self._conll_coref_scores = ConllCorefScores()
        if lexical_dropout > 0:
            self._lexical_dropout = torch.nn.Dropout(p=lexical_dropout)
        else:
            self._lexical_dropout = lambda x: x
        initializer(self)

    #overrides
    def forward(self,  # type: ignore
                text                             ,
                spans                 ,
                span_labels                  = None,
                metadata                       = None)                           :
        # pylint: disable=arguments-differ
        u"""
        Parameters
        ----------
        text : ``Dict[str, torch.LongTensor]``, required.
            The output of a ``TextField`` representing the text of
            the document.
        spans : ``torch.IntTensor``, required.
            A tensor of shape (batch_size, num_spans, 2), representing the inclusive start and end
            indices of candidate spans for mentions. Comes from a ``ListField[SpanField]`` of
            indices into the text of the document.
        span_labels : ``torch.IntTensor``, optional (default = None)
            A tensor of shape (batch_size, num_spans), representing the cluster ids
            of each span, or -1 for those which do not appear in any clusters.

        Returns
        -------
        An output dictionary consisting of:
        top_spans : ``torch.IntTensor``
            A tensor of shape ``(batch_size, num_spans_to_keep, 2)`` representing
            the start and end word indices of the top spans that survived the pruning stage.
        antecedent_indices : ``torch.IntTensor``
            A tensor of shape ``(num_spans_to_keep, max_antecedents)`` representing for each top span
            the index (with respect to top_spans) of the possible antecedents the model considered.
        predicted_antecedents : ``torch.IntTensor``
            A tensor of shape ``(batch_size, num_spans_to_keep)`` representing, for each top span, the
            index (with respect to antecedent_indices) of the most likely antecedent. -1 means there
            was no predicted link.
        loss : ``torch.FloatTensor``, optional
            A scalar loss to be optimised.
        """
        # Shape: (batch_size, document_length, embedding_size)
        text_embeddings = self._lexical_dropout(self._text_field_embedder(text))

        document_length = text_embeddings.size(1)
        num_spans = spans.size(1)

        # Shape: (batch_size, document_length)
        text_mask = util.get_text_field_mask(text).float()

        # Shape: (batch_size, num_spans)
        span_mask = (spans[:, :, 0] >= 0).squeeze(-1).float()
        # SpanFields return -1 when they are used as padding. As we do
        # some comparisons based on span widths when we attend over the
        # span representations that we generate from these indices, we
        # need them to be <= 0. This is only relevant in edge cases where
        # the number of spans we consider after the pruning stage is >= the
        # total number of spans, because in this case, it is possible we might
        # consider a masked span.
        # Shape: (batch_size, num_spans, 2)
        spans = F.relu(spans.float()).long()

        # Shape: (batch_size, document_length, encoding_dim)
        contextualized_embeddings = self._context_layer(text_embeddings, text_mask)
        # Shape: (batch_size, num_spans, 2 * encoding_dim + feature_size)
        endpoint_span_embeddings = self._endpoint_span_extractor(contextualized_embeddings, spans)
        # Shape: (batch_size, num_spans, emebedding_size)
        attended_span_embeddings = self._attentive_span_extractor(text_embeddings, spans)

        # Shape: (batch_size, num_spans, emebedding_size + 2 * encoding_dim + feature_size)
        span_embeddings = torch.cat([endpoint_span_embeddings, attended_span_embeddings], -1)

        # Prune based on mention scores.
        num_spans_to_keep = int(math.floor(self._spans_per_word * document_length))

        (top_span_embeddings, top_span_mask,
         top_span_indices, top_span_mention_scores) = self._mention_pruner(span_embeddings,
                                                                           span_mask,
                                                                           num_spans_to_keep)
        top_span_mask = top_span_mask.unsqueeze(-1)
        # Shape: (batch_size * num_spans_to_keep)
        # torch.index_select only accepts 1D indices, but here
        # we need to select spans for each element in the batch.
        # This reformats the indices to take into account their
        # index into the batch. We precompute this here to make
        # the multiple calls to util.batched_index_select below more efficient.
        flat_top_span_indices = util.flatten_and_batch_shift_indices(top_span_indices, num_spans)

        # Compute final predictions for which spans to consider as mentions.
        # Shape: (batch_size, num_spans_to_keep, 2)
        top_spans = util.batched_index_select(spans,
                                              top_span_indices,
                                              flat_top_span_indices)

        # Compute indices for antecedent spans to consider.
        max_antecedents = min(self._max_antecedents, num_spans_to_keep)

        # Now that we have our variables in terms of num_spans_to_keep, we need to
        # compare span pairs to decide each span's antecedent. Each span can only
        # have prior spans as antecedents, and we only consider up to max_antecedents
        # prior spans. So the first thing we do is construct a matrix mapping a span's
        #  index to the indices of its allowed antecedents. Note that this is independent
        #  of the batch dimension - it's just a function of the span's position in
        # top_spans. The spans are in document order, so we can just use the relative
        # index of the spans to know which other spans are allowed antecedents.

        # Once we have this matrix, we reformat our variables again to get embeddings
        # for all valid antecedents for each span. This gives us variables with shapes
        #  like (batch_size, num_spans_to_keep, max_antecedents, embedding_size), which
        #  we can use to make coreference decisions between valid span pairs.

        # Shapes:
        # (num_spans_to_keep, max_antecedents),
        # (1, max_antecedents),
        # (1, num_spans_to_keep, max_antecedents)
        valid_antecedent_indices, valid_antecedent_offsets, valid_antecedent_log_mask =\
            self._generate_valid_antecedents(num_spans_to_keep, max_antecedents, util.get_device_of(text_mask))
        # Select tensors relating to the antecedent spans.
        # Shape: (batch_size, num_spans_to_keep, max_antecedents, embedding_size)
        candidate_antecedent_embeddings = util.flattened_index_select(top_span_embeddings,
                                                                      valid_antecedent_indices)

        # Shape: (batch_size, num_spans_to_keep, max_antecedents)
        candidate_antecedent_mention_scores = util.flattened_index_select(top_span_mention_scores,
                                                                          valid_antecedent_indices).squeeze(-1)
        # Compute antecedent scores.
        # Shape: (batch_size, num_spans_to_keep, max_antecedents, embedding_size)
        span_pair_embeddings = self._compute_span_pair_embeddings(top_span_embeddings,
                                                                  candidate_antecedent_embeddings,
                                                                  valid_antecedent_offsets)
        # Shape: (batch_size, num_spans_to_keep, 1 + max_antecedents)
        coreference_scores = self._compute_coreference_scores(span_pair_embeddings,
                                                              top_span_mention_scores,
                                                              candidate_antecedent_mention_scores,
                                                              valid_antecedent_log_mask)

        # We now have, for each span which survived the pruning stage,
        # a predicted antecedent. This implies a clustering if we group
        # mentions which refer to each other in a chain.
        # Shape: (batch_size, num_spans_to_keep)
        _, predicted_antecedents = coreference_scores.max(2)
        # Subtract one here because index 0 is the "no antecedent" class,
        # so this makes the indices line up with actual spans if the prediction
        # is greater than -1.
        predicted_antecedents -= 1

        output_dict = {u"top_spans": top_spans,
                       u"antecedent_indices": valid_antecedent_indices,
                       u"predicted_antecedents": predicted_antecedents}
        if span_labels is not None:
            # Find the gold labels for the spans which we kept.
            pruned_gold_labels = util.batched_index_select(span_labels.unsqueeze(-1),
                                                           top_span_indices,
                                                           flat_top_span_indices)

            antecedent_labels = util.flattened_index_select(pruned_gold_labels,
                                                            valid_antecedent_indices).squeeze(-1)
            antecedent_labels += valid_antecedent_log_mask.long()

            # Compute labels.
            # Shape: (batch_size, num_spans_to_keep, max_antecedents + 1)
            gold_antecedent_labels = self._compute_antecedent_gold_labels(pruned_gold_labels,
                                                                          antecedent_labels)
            # Now, compute the loss using the negative marginal log-likelihood.
            # This is equal to the log of the sum of the probabilities of all antecedent predictions
            # that would be consistent with the data, in the sense that we are minimising, for a
            # given span, the negative marginal log likelihood of all antecedents which are in the
            # same gold cluster as the span we are currently considering. Each span i predicts a
            # single antecedent j, but there might be several prior mentions k in the same
            # coreference cluster that would be valid antecedents. Our loss is the sum of the
            # probability assigned to all valid antecedents. This is a valid objective for
            # clustering as we don't mind which antecedent is predicted, so long as they are in
            #  the same coreference cluster.
            coreference_log_probs = util.last_dim_log_softmax(coreference_scores, top_span_mask)
            correct_antecedent_log_probs = coreference_log_probs + gold_antecedent_labels.log()
            negative_marginal_log_likelihood = -util.logsumexp(correct_antecedent_log_probs).sum()

            self._mention_recall(top_spans, metadata)
            self._conll_coref_scores(top_spans, valid_antecedent_indices, predicted_antecedents, metadata)

            output_dict[u"loss"] = negative_marginal_log_likelihood

        if metadata is not None:
            output_dict[u"document"] = [x[u"original_text"] for x in metadata]
        return output_dict

    #overrides
    def decode(self, output_dict                         ):
        u"""
        Converts the list of spans and predicted antecedent indices into clusters
        of spans for each element in the batch.

        Parameters
        ----------
        output_dict : ``Dict[str, torch.Tensor]``, required.
            The result of calling :func:`forward` on an instance or batch of instances.

        Returns
        -------
        The same output dictionary, but with an additional ``clusters`` key:

        clusters : ``List[List[List[Tuple[int, int]]]]``
            A nested list, representing, for each instance in the batch, the list of clusters,
            which are in turn comprised of a list of (start, end) inclusive spans into the
            original document.
        """

        # A tensor of shape (batch_size, num_spans_to_keep, 2), representing
        # the start and end indices of each span.
        batch_top_spans = output_dict[u"top_spans"].detach().cpu()

        # A tensor of shape (batch_size, num_spans_to_keep) representing, for each span,
        # the index into ``antecedent_indices`` which specifies the antecedent span. Additionally,
        # the index can be -1, specifying that the span has no predicted antecedent.
        batch_predicted_antecedents = output_dict[u"predicted_antecedents"].detach().cpu()

        # A tensor of shape (num_spans_to_keep, max_antecedents), representing the indices
        # of the predicted antecedents with respect to the 2nd dimension of ``batch_top_spans``
        # for each antecedent we considered.
        antecedent_indices = output_dict[u"antecedent_indices"].detach().cpu()
        batch_clusters                                    = []

        # Calling zip() on two tensors results in an iterator over their
        # first dimension. This is iterating over instances in the batch.
        for top_spans, predicted_antecedents in izip(batch_top_spans, batch_predicted_antecedents):
            spans_to_cluster_ids                             = {}
            clusters                              = []

            for i, (span, predicted_antecedent) in enumerate(izip(top_spans, predicted_antecedents)):
                if predicted_antecedent < 0:
                    # We don't care about spans which are
                    # not co-referent with anything.
                    continue

                # Find the right cluster to update with this span.
                # To do this, we find the row in ``antecedent_indices``
                # corresponding to this span we are considering.
                # The predicted antecedent is then an index into this list
                # of indices, denoting the span from ``top_spans`` which is the
                # most likely antecedent.
                predicted_index = antecedent_indices[i, predicted_antecedent]

                antecedent_span = (top_spans[predicted_index, 0].item(),
                                   top_spans[predicted_index, 1].item())

                # Check if we've seen the span before.
                if antecedent_span in spans_to_cluster_ids:
                    predicted_cluster_id      = spans_to_cluster_ids[antecedent_span]
                else:
                    # We start a new cluster.
                    predicted_cluster_id = len(clusters)
                    # Append a new cluster containing only this span.
                    clusters.append([antecedent_span])
                    # Record the new id of this span.
                    spans_to_cluster_ids[antecedent_span] = predicted_cluster_id

                # Now add the span we are currently considering.
                span_start, span_end = span[0].item(), span[1].item()
                clusters[predicted_cluster_id].append((span_start, span_end))
                spans_to_cluster_ids[(span_start, span_end)] = predicted_cluster_id
            batch_clusters.append(clusters)

        output_dict[u"clusters"] = batch_clusters
        return output_dict

    #overrides
    def get_metrics(self, reset       = False)                    :
        mention_recall = self._mention_recall.get_metric(reset)
        coref_precision, coref_recall, coref_f1 = self._conll_coref_scores.get_metric(reset)

        return {u"coref_precision": coref_precision,
                u"coref_recall": coref_recall,
                u"coref_f1": coref_f1,
                u"mention_recall": mention_recall}

    @staticmethod
    def _generate_valid_antecedents(num_spans_to_keep     ,
                                    max_antecedents     ,
                                    device     ):

        u"""
        This method generates possible antecedents per span which survived the pruning
        stage. This procedure is `generic across the batch`. The reason this is the case is
        that each span in a batch can be coreferent with any previous span, but here we
        are computing the possible `indices` of these spans. So, regardless of the batch,
        the 1st span _cannot_ have any antecedents, because there are none to select from.
        Similarly, each element can only predict previous spans, so this returns a matrix
        of shape (num_spans_to_keep, max_antecedents), where the (i,j)-th index is equal to
        (i - 1) - j if j <= i, or zero otherwise.

        Parameters
        ----------
        num_spans_to_keep : ``int``, required.
            The number of spans that were kept while pruning.
        max_antecedents : ``int``, required.
            The maximum number of antecedent spans to consider for every span.
        device: ``int``, required.
            The CUDA device to use.

        Returns
        -------
        valid_antecedent_indices : ``torch.IntTensor``
            The indices of every antecedent to consider with respect to the top k spans.
            Has shape ``(num_spans_to_keep, max_antecedents)``.
        valid_antecedent_offsets : ``torch.IntTensor``
            The distance between the span and each of its antecedents in terms of the number
            of considered spans (i.e not the word distance between the spans).
            Has shape ``(1, max_antecedents)``.
        valid_antecedent_log_mask : ``torch.FloatTensor``
            The logged mask representing whether each antecedent span is valid. Required since
            different spans have different numbers of valid antecedents. For example, the first
            span in the document should have no valid antecedents.
            Has shape ``(1, num_spans_to_keep, max_antecedents)``.
        """
        # Shape: (num_spans_to_keep, 1)
        target_indices = util.get_range_vector(num_spans_to_keep, device).unsqueeze(1)

        # Shape: (1, max_antecedents)
        valid_antecedent_offsets = (util.get_range_vector(max_antecedents, device) + 1).unsqueeze(0)

        # This is a broadcasted subtraction.
        # Shape: (num_spans_to_keep, max_antecedents)
        raw_antecedent_indices = target_indices - valid_antecedent_offsets

        # In our matrix of indices, the upper triangular part will be negative
        # because the offsets will be > the target indices. We want to mask these,
        # because these are exactly the indices which we don't want to predict, per span.
        # We're generating a logspace mask here because we will eventually create a
        # distribution over these indices, so we need the 0 elements of the mask to be -inf
        # in order to not mess up the normalisation of the distribution.
        # Shape: (1, num_spans_to_keep, max_antecedents)
        valid_antecedent_log_mask = (raw_antecedent_indices >= 0).float().unsqueeze(0).log()

        # Shape: (num_spans_to_keep, max_antecedents)
        valid_antecedent_indices = F.relu(raw_antecedent_indices.float()).long()
        return valid_antecedent_indices, valid_antecedent_offsets, valid_antecedent_log_mask

    def _compute_span_pair_embeddings(self,
                                      top_span_embeddings                   ,
                                      antecedent_embeddings                   ,
                                      antecedent_offsets                   ):
        u"""
        Computes an embedding representation of pairs of spans for the pairwise scoring function
        to consider. This includes both the original span representations, the element-wise
        similarity of the span representations, and an embedding representation of the distance
        between the two spans.

        Parameters
        ----------
        top_span_embeddings : ``torch.FloatTensor``, required.
            Embedding representations of the top spans. Has shape
            (batch_size, num_spans_to_keep, embedding_size).
        antecedent_embeddings : ``torch.FloatTensor``, required.
            Embedding representations of the antecedent spans we are considering
            for each top span. Has shape
            (batch_size, num_spans_to_keep, max_antecedents, embedding_size).
        antecedent_offsets : ``torch.IntTensor``, required.
            The offsets between each top span and its antecedent spans in terms
            of spans we are considering. Has shape (1, max_antecedents).

        Returns
        -------
        span_pair_embeddings : ``torch.FloatTensor``
            Embedding representation of the pair of spans to consider. Has shape
            (batch_size, num_spans_to_keep, max_antecedents, embedding_size)
        """
        # Shape: (batch_size, num_spans_to_keep, max_antecedents, embedding_size)
        target_embeddings = top_span_embeddings.unsqueeze(2).expand_as(antecedent_embeddings)

        # Shape: (1, max_antecedents, embedding_size)
        antecedent_distance_embeddings = self._distance_embedding(
                util.bucket_values(antecedent_offsets,
                                   num_total_buckets=self._num_distance_buckets))

        # Shape: (1, 1, max_antecedents, embedding_size)
        antecedent_distance_embeddings = antecedent_distance_embeddings.unsqueeze(0)

        expanded_distance_embeddings_shape = (antecedent_embeddings.size(0),
                                              antecedent_embeddings.size(1),
                                              antecedent_embeddings.size(2),
                                              antecedent_distance_embeddings.size(-1))
        # Shape: (batch_size, num_spans_to_keep, max_antecedents, embedding_size)
        antecedent_distance_embeddings = antecedent_distance_embeddings.expand(*expanded_distance_embeddings_shape)

        # Shape: (batch_size, num_spans_to_keep, max_antecedents, embedding_size)
        span_pair_embeddings = torch.cat([target_embeddings,
                                          antecedent_embeddings,
                                          antecedent_embeddings * target_embeddings,
                                          antecedent_distance_embeddings], -1)
        return span_pair_embeddings

    @staticmethod
    def _compute_antecedent_gold_labels(top_span_labels                 ,
                                        antecedent_labels                 ):
        u"""
        Generates a binary indicator for every pair of spans. This label is one if and
        only if the pair of spans belong to the same cluster. The labels are augmented
        with a dummy antecedent at the zeroth position, which represents the prediction
        that a span does not have any antecedent.

        Parameters
        ----------
        top_span_labels : ``torch.IntTensor``, required.
            The cluster id label for every span. The id is arbitrary,
            as we just care about the clustering. Has shape (batch_size, num_spans_to_keep).
        antecedent_labels : ``torch.IntTensor``, required.
            The cluster id label for every antecedent span. The id is arbitrary,
            as we just care about the clustering. Has shape
            (batch_size, num_spans_to_keep, max_antecedents).

        Returns
        -------
        pairwise_labels_with_dummy_label : ``torch.FloatTensor``
            A binary tensor representing whether a given pair of spans belong to
            the same cluster in the gold clustering.
            Has shape (batch_size, num_spans_to_keep, max_antecedents + 1).

        """
        # Shape: (batch_size, num_spans_to_keep, max_antecedents)
        target_labels = top_span_labels.expand_as(antecedent_labels)
        same_cluster_indicator = (target_labels == antecedent_labels).float()
        non_dummy_indicator = (target_labels >= 0).float()
        pairwise_labels = same_cluster_indicator * non_dummy_indicator

        # Shape: (batch_size, num_spans_to_keep, 1)
        dummy_labels = (1 - pairwise_labels).prod(-1, keepdim=True)

        # Shape: (batch_size, num_spans_to_keep, max_antecedents + 1)
        pairwise_labels_with_dummy_label = torch.cat([dummy_labels, pairwise_labels], -1)
        return pairwise_labels_with_dummy_label

    def _compute_coreference_scores(self,
                                    pairwise_embeddings                   ,
                                    top_span_mention_scores                   ,
                                    antecedent_mention_scores                   ,
                                    antecedent_log_mask                   )                     :
        u"""
        Computes scores for every pair of spans. Additionally, a dummy label is included,
        representing the decision that the span is not coreferent with anything. For the dummy
        label, the score is always zero. For the true antecedent spans, the score consists of
        the pairwise antecedent score and the unary mention scores for the span and its
        antecedent. The factoring allows the model to blame many of the absent links on bad
        spans, enabling the pruning strategy used in the forward pass.

        Parameters
        ----------
        pairwise_embeddings: ``torch.FloatTensor``, required.
            Embedding representations of pairs of spans. Has shape
            (batch_size, num_spans_to_keep, max_antecedents, encoding_dim)
        top_span_mention_scores: ``torch.FloatTensor``, required.
            Mention scores for every span. Has shape
            (batch_size, num_spans_to_keep, max_antecedents).
        antecedent_mention_scores: ``torch.FloatTensor``, required.
            Mention scores for every antecedent. Has shape
            (batch_size, num_spans_to_keep, max_antecedents).
        antecedent_log_mask: ``torch.FloatTensor``, required.
            The log of the mask for valid antecedents.

        Returns
        -------
        coreference_scores: ``torch.FloatTensor``
            A tensor of shape (batch_size, num_spans_to_keep, max_antecedents + 1),
            representing the unormalised score for each (span, antecedent) pair
            we considered.

        """
        # Shape: (batch_size, num_spans_to_keep, max_antecedents)
        antecedent_scores = self._antecedent_scorer(
                self._antecedent_feedforward(pairwise_embeddings)).squeeze(-1)
        antecedent_scores += top_span_mention_scores + antecedent_mention_scores
        antecedent_scores += antecedent_log_mask

        # Shape: (batch_size, num_spans_to_keep, 1)
        shape = [antecedent_scores.size(0), antecedent_scores.size(1), 1]
        dummy_scores = antecedent_scores.new_zeros(*shape)

        # Shape: (batch_size, num_spans_to_keep, max_antecedents + 1)
        coreference_scores = torch.cat([dummy_scores, antecedent_scores], -1)
        return coreference_scores

CoreferenceResolver = Model.register(u"coref")(CoreferenceResolver)


import django_rq
import logging
from datetime import datetime, timedelta
import time

from django.core.management.base import BaseCommand
from django_rq import job
from reviews.models import Review

#@job
def get_count_reviews():
    logger = logging.getLogger('counter')
    count = Review.objects.count()
    time.sleep(4)
    if count:
        logger.info(f'Всего отзывов в системе: {count}')
    else:
        logger.error('Something went wrong!')

class Command(BaseCommand):
    help = "Выводит на экран и в лог количество записей в таблице Review"

    def handle(self, *args, **options):
        scheduler = django_rq.get_scheduler('default')
        scheduler.schedule(
            scheduled_time=datetime.now(),
            func=get_count_reviews,
            interval=10,
            repeat=4,
        )


from nltk.stem import WordNetLemmatizer
import re
from nltk.tokenize import word_tokenize, sent_tokenize

def myTokenizer(readFileDir, saveFileDir, stopwords, lim):
    readFile = open(readFileDir, "r", encoding="UTF8")
    saveFile = open(saveFileDir, "w", encoding="UTF8")
    preprocessed = []
    lemmatizer = WordNetLemmatizer()
    while True:
        line = readFile.readline()
        if len(line) == 0:
            print("File read finished")
            readFile.close()
            break
        sentTokenized = sent_tokenize(line)

        for sent in sentTokenized:
            sent = re.sub("[^a-zA-Z]", " ", sent)
            wordTokenized = word_tokenize(sent)
            i = 0
            while i < len(wordTokenized):
                if len(wordTokenized[i]) <= lim or \
                        wordTokenized[i] in stopwords:
                    wordTokenized.remove(wordTokenized[i])
                else:
                    wordTokenized[i] = wordTokenized[i].lower()
                    wordTokenized[i] = lemmatizer.lemmatize(wordTokenized[i])
                    saveFile.write(wordTokenized[i])
                    if i < len(wordTokenized) - 1:
                        saveFile.write(" ")
                    i += 1

            saveFile.write("\n")
            preprocessed.append(wordTokenized)

    saveFile.close()

    return preprocessed


import time
from datetime import timedelta

import requests
from celery import Celery
from celery.utils.log import get_task_logger

app = Celery('parsing')
app.conf.update(broker_url='amqp://guest:guest@rabbitmq:5672', broker_connection_retry_on_startup=True)
app.conf.beat_schedule = {
    'add-every-monday-morning': {
        'task': 'parsing',
        'schedule': timedelta(seconds=15)
    },
}
# crontab(seconds='*/15')
celery_logger = get_task_logger(__name__)

@app.task(name='parsing')
def parse_excel_task():
    time.sleep(5)
    print('zalupa')
    celery_logger.info('parsing')
    requests.post('http://api:8000/api/v1/parser/parse-excel')


from django.contrib import admin,messages
from .models import *
from inline_actions.admin import InlineActionsModelAdminMixin
from commun.util import Util
from django.utils.translation import ugettext_lazy as _
from django.conf import settings

media_root = settings.MEDIA_ROOT
admin.site.site_header = "Administration ERP"
admin.site.site_title = "ERP"
admin.site.index_title = "Bienvenu à l'ERP"

@admin.register(Expediteur)
class ExpediteurAdmin(admin.ModelAdmin):
    list_display = ("name","structure","employe")
    search_fields = ("name",)

@admin.register(TypeCourier)
class TypeCourierAdmin(admin.ModelAdmin):
    list_display = ("name",)

@admin.register(Classification)
class ClassificationAdmin(admin.ModelAdmin):
    list_display = ("name",)

@admin.register(Status)
class StatusAdmin(admin.ModelAdmin):
    list_display = ("name",)

@admin.register(Attachment)
class AttachmentAdmin(admin.ModelAdmin):
    list_display = ("name","file",)
##############
class AttachmentTabular(admin.TabularInline):
    model = Attachment
    extra = 1
@admin.register(Courier)
class CourierAdmin(InlineActionsModelAdminMixin,admin.ModelAdmin):
    inlines = [AttachmentTabular]
    autocomplete_fields = ("expediteur","destinataires","visible_a")
    inline_actions = ('send_by_mail','delete')
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return  qs.filter(deleted=False)
    def get_list_display(self, request):
        list=("objet","expediteur","date_expedition","date_arrivee","type","classification","status")
        if request.user.is_superuser:
            return list+('deleted',)+ (super().get_list_display(request).pop(),)
        return list+(super().get_list_display(request).pop(),)

    def send_by_mail(self, request, obj:Courier, parent_obj):
        dists=obj.visible_a.all()
        to=[]
        for dist in dists:
            to.append(dist.getExpediteur().email)
        source=obj.expediteur.getExpediteur().email
        attachments=list(obj.attachments.all())
        arr=list()
        files=list()
        for dist in dists:
            arr.append(dist.email)
        for attch in attachments:
            files.append(attch.file.name)
        try:
            res=Util.send_email(subject=obj.objet, message=obj.objet, source=source, to=to, cc=[], attachments=files)
            messages.success(request, _("Email Envoyé"))
        except Exception :
            messages.error(request, _("Erreur d'envoie"))

    send_by_mail.short_description = 'envoyer email'

    def delete(self, request, obj:Courier, parent_obj):
        obj.deleted=True
        obj.save()
    delete.short_description = 'supprimer'


from ply.lex import LexToken

from windyquery.validator import ValidationError
from ._base import Base

TOKEN = 'SCHEMA'

class SchemaToken(LexToken):
    def __init__(self, value):
        self.type = TOKEN
        self.value = value
        self.lineno = 0
        self.lexpos = 0

class Schema(Base):
    def schema(self, s: str):
        try:
            s = self.validator.validate_schema(s)
        except ValidationError as err:
            raise UserWarning(f'invalid schema: {err}') from None
        self.append(SchemaToken(s))


import tkinter as tk

import customtkinter as ctk
from customtkinter import ThemeManager

from View.GUI.CustomWidgets.NotebookCloseableTabs import NotebookCloseableTabs
from View.GUI.Windows.ResultWindow.ComputationStatusBoard import ComputationStatusBoard
from View.Observer import Observer

class ComputationNotebook(NotebookCloseableTabs, Observer):

    def __init__(self, parent, computation, controller):
        fg_color = ThemeManager.theme["color_scale"]["inner"]
        super().__init__(parent, color_notebook_bg=fg_color)

        self.add_widget(ComputationStatusBoard(self, computation, controller), "Status Board")
        self.results = []
        self.handler_id = None
        self.subscribed_results = []

        Observer.__init__(self, computation)
        for result in computation.results:
            if result.is_finished:
                self.results.append(result)
            else:
                result.subscribe(self)
                self.subscribed_results.append(result)
        self.handle_tasks()

    def update_(self, updated_component):
        if updated_component[1] == "finished_result":
            result = updated_component[0]
            self.results.append(result)

    def start_task_handler(self):
        """
        Starts the task handler.
        """
        self.handler_id = self.after(2000, self.handle_tasks)

    def handle_tasks(self):
        """
        Handles received results.
        """
        for result in list(self.results):
            self.add_result(result)
            self.results.remove(result)

        self.start_task_handler()

    def destroy(self):
        self.after_cancel(self.handler_id)
        self.observed_subject.unsubscribe(self)
        self.observed_subject = None
        for result in self.subscribed_results:
            result.unsubscribe(self)
        self.subscribed_results.clear()
        super().destroy()

    def add_result(self, result):
        """
        Adds results to the notebook.

        :param result: result object
        """
        fg_color = ThemeManager.theme["color_scale"]["inner"]
        textbox = ctk.CTkTextbox(self, fg_color=fg_color, bg_color=fg_color, wrap="word")
        textbox.insert(tk.END, result.result_text)
        self.add_widget(textbox, result.configuration_name)


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

t = np.linspace(0, 2*np.pi, 20)
p = np.linspace(0,   np.pi, 10)

theta,phi = np.meshgrid(t,p)

x = np.cos(theta)*np.sin(phi)
y = np.sin(theta)*np.sin(phi)
z = np.cos(phi)

fig = plt.figure(figsize=(10,4))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122, projection='3d')

ax1.plot(theta.flatten(), phi.flatten(), 'o')
ax1.set_xlabel("$\\theta$")
ax1.set_ylabel("$\\phi$")

ax2.plot_surface(x,y,z, edgecolors='0.2')

plt.show()