#!/usr/bin/python
# -*- coding: utf-8 -*-
# vim: tabstop=4:softtabstop=4:shiftwidth=4:expandtab

"""
Plk: Qt interactive emulator of the tempo2 plk emulator

Help from tempo2 plk:

Fitting and Calculating Options
===============================
b          Bin TOAs within certain time bin
c          Change fitting parameters
d (or right mouse) delete point
ctrl-d     delete highlighted points
e          multiply all TOA errors by given amount
F          run FITWAVES
ctrl-f     remove FITWAVES curve from residuals
i (or left mouse) identify point
M          toggle removing mean from the residuals
ctrl-n     Add white noise to site-arrival-times
p          Change model parameter values
ctrl-p     Toggle plotting versus pulse phase
r          Reset (reload .par and .tim file)
ctrl-r     Select regions in MJDs and write to file
w          toggle fitting using weights
x          redo fit using post-fit parameters
+          add positive phase jump
-          add negative phase jump
BACKSPACE  remove all phase jumps
ctrl-=     add period to residuals above cursor
/          re-read .par file

Plot Selection
==============
D (or middle mouse) view profile
s          start of zoom section
f          finish of zoom section
Ctrl-u     Overplot Shapiro delay
u          unzoom
v          view profiles for highlighted points
V          define the user parameter
Ctrl-v     for pre-fit plotting, decompose the timing model fits
           (i.e. overplot the fitted curves - only for prefit plots
ctrl-X     select x-axis specifically
y          Rescale y-axis only
Y          set y-scale exactly
ctrl-Y     select y-axis specifically
z          Zoom using mouse
<          in zoom mode include previous observation
>          in zoom mode include next observation
1          plot pre-fit  residuals vs date
2          plot post-fit residuals vs date
3          plot pre-fit  residuals vs orbital phase
4          plot post-fit residuals vs orbital phase
5          plot pre-fit  residuals serially
6          plot post-fit residuals serially
7          plot pre-fit  residuals vs day of year
8          plot post-fit residuals vs day of year
9          plot pre-fit  residuals vs frequency
a          plot post-fit residuals vs frequency
!          plot pre-fit  residuals vs TOA error
@          plot post-fit residuals vs TOA error
#          plot pre-fit  residuals vs user values
$          plot post-fit residuals vs user values
%          plot pre-fit  residuals vs year
^          plot post-fit residuals vs year
&          plot pre-fit residuals vs elevation
*          plot post-fit residuals vs elevation
(          plot pre-fit residuals vs rounded MJD
)          plot post-fit residuals vs rounded MJD

Options for selecting x and y axes individually
Ctrl-X n   set x-axis
Ctrl-Y n   set y-axis
where n =

1         plot pre-fit residuals
2         plot post-fit residuals
3         plot centred MJD
4         plot orbital phase
5         plot TOA number
6         plot day of year
7         plot frequency
8         plot TOA error
9         plot user value
0         plot year
-         plot elevation

Display Options
===============
B          place periodic marks on the x-scale
ctrl-c     Toggle between period epoch and centre for the reference epoch
E          toggle plotting error bars
g          change graphics device
G          change gridding on graphics device
ctrl-e     highlight points more than 3 sigma from the mean
H          highlight points with specific flag using symbols
ctrl-i     highlight points with specific flag using colours
I          indicate individual observations
j          draw line between points
J          toggle plotting points
L          add label to plot
ctrl-l     add line to plot
ctrl-m     toggle menu bar
N          highlight point with a given filename
o          obtain/highlight all points currently in plot
ctrl-T     set text size
U          unhighlight selected points
[          toggle plotting x-axis on log scale
]          toggle plotting y-axis on log scale

Output Options
==============
Ctrl-J     output listing of residuals in Jodrell format
Ctrl-O     output listing of residuals in simple format
l          list all data points in zoomed region
m          measure distance between two points
P          write new .par file
Ctrl-w     over-write input .par file
S          save a new .tim file
Ctrl-S     overwrite input.tim file
t          Toggle displaying statistics for zoomed region
Ctrl-z     Listing of all highlighted points

Various Options
===============
C          run unix command with filenames for highlighted observations
h          this help file
q          quit

"""

from __future__ import print_function
from __future__ import division
import os, sys

# Importing all the stuff for the IPython console widget
try:
    raise ImportError("")
    from PyQt4 import QtGui, QtCore
except ImportError:
    from qtconsole.qt import QtCore, QtGui

# Importing all the stuff for the matplotlib widget
import matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
try:
    from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
except ImportError:
    from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar

from matplotlib.figure import Figure

# Numpy etc.
import numpy as np
import time
import copy

# For date conversions
import jdcal        # pip install jdcal

import constants
import qtpulsar as qp
import pysolvepulsar as psp

# All the types of quantities we will be able to plot in the plk widget
plot_labels = ['Residuals', 'mjd', 'year', 'pulse phase', 'orbital phase', \
               'serial', 'day of year', 'frequency', 'TOA error', 'elevation', \
            'rounded MJD', 'sidereal time', 'hour angle', 'para. angle']
nofitboxpars = ['START', 'FINISH', 'POSEPOCH', 'PEPOCH', 'DMEPOCH', \
            'EPHVER', 'TZRMJD', 'TZRFRQ', 'TRES']
plotrangestretch = 1.05

# Design philosophy:
# - The pulsar timing engine is dealt with through derivatives of the abstract
# base class 'BasePulsar'. The object is called psr. Interface is close to that
# of libstempo, but it has some extra features related to plotting parameters.
# PlkWidget has psr as a member, but all the child widgets should not (they do
# know about psr at the moment).
# The plotting parameters and all that are obtained through the psr object. No
# calculations whatsoever are supposed to be done in PlkWidget, or it's child
# widget. They need to know as little as possible, so that they are reusable in
# other GUI types.
# Drawing is done through PlkWidget. There is a callback function 'updatePlot'
# that all child widgets are allowed to call, but they should not get access to
# any further data.

class PlkActionsWidget(QtGui.QWidget):
    """
    A widget that shows some action items, like re-fit, write par, write tim,
    etc.  These items are shown as buttons
    """

    def __init__(self, parent=None, **kwargs):
        super(PlkActionsWidget, self).__init__(parent, **kwargs)

        self.parent = parent
        self.updatePlot = None
        self.reFit_callback = None

        self.hbox = QtGui.QHBoxLayout()     # One horizontal layout

        self.setPlkActionsWidget()

    def setPlkActionsWidget(self):
        button = QtGui.QPushButton('Re-fit')
        button.clicked.connect(self.reFit)
        self.hbox.addWidget(button)

        button = QtGui.QPushButton('Clear')
        button.clicked.connect(self.clearAll)
        self.hbox.addWidget(button)

        button = QtGui.QPushButton('Write par')
        button.clicked.connect(self.writePar)
        self.hbox.addWidget(button)

        button = QtGui.QPushButton('Write tim')
        button.clicked.connect(self.writeTim)
        self.hbox.addWidget(button)

        button = QtGui.QPushButton('Save fig')
        button.clicked.connect(self.saveFig)
        self.hbox.addWidget(button)

        self.hbox.addStretch(1)

        self.setLayout(self.hbox)

    def setCallbacks(self, updatePlot, reFit):
        """
        Callback functions
        """
        self.updatePlot = updatePlot
        self.reFit_callback = reFit

    def reFit(self):
        if self.reFit_callback is not None:
            self.reFit_callback()

    def writePar(self):
        print("Write Par clicked")

    def writeTim(self):
        print("Write Tim clicked")

    def clearAll(self):
        print("Clear clicked")

    def saveFig(self):
        print("Save fig clicked")

class PlkFitboxesWidget(QtGui.QWidget):
    """
    A widget that allows one to select which parameters to fit for
    """

    def __init__(self, parent=None, **kwargs):
        super(PlkFitboxesWidget, self).__init__(parent, **kwargs)

        self.parent = parent
        self.boxChecked = None

        # The checkboxes are ordered on a grid
        self.hbox = QtGui.QHBoxLayout()     # One horizontal layout
        self.vboxes = []                    # Several vertical layouts (9 per line)
        self.fitboxPerLine = 8

        self.setPlkFitboxesLayout()

    def setPlkFitboxesLayout(self):
        """
        Set the layout of this widget
        """
        # Initialise the layout of the fit-box Widget
        # Initially there are no fitboxes, so just add the hbox
        self.setLayout(self.hbox)

    def setCallbacks(self, boxChecked, setpars, fitpars, nofitbox):
        """
        Set the callback functions

        @param boxChecked:      Callback function, when box is checked
        @param setpars:         psr.parameters(which='set')
        @param fitpars:         psr.parameters(which='fit')
        @param nofitbox:        Which parameters not to have a box for
        """
        self.boxChecked = boxChecked
        self.deleteFitCheckBoxes()
        self.addFitCheckBoxes(setpars, fitpars, nofitbox)

    def addFitCheckBoxes(self, setpars, fitpars, nofitbox):
        """
        Add the fitting checkboxes at the top of the plk Window

        @param setpars:     The parameters that are 'set' (in the model)
        @param fitpars:     The parameters that are currently being fitted for
        @param nofitbox:    The parameters we should skip
        """
        # Delete the fitboxes if there were still some left
        if not len(self.vboxes) == 0:
            self.deleteFitCheckBoxes()

        # First add all the vbox layouts
        for ii in range(min(self.fitboxPerLine, len(setpars))):
            self.vboxes.append(QtGui.QVBoxLayout())
            self.hbox.addLayout(self.vboxes[-1])

        # Then add the checkbox widgets to the vboxes
        index = 0
        for pp, par in enumerate(setpars):
            if not par in nofitbox:
                vboxind = index % self.fitboxPerLine

                cb = QtGui.QCheckBox(par, self)
                cb.stateChanged.connect(self.changedFitCheckBox)

                # Set checked/unchecked
                cb.setChecked(par in fitpars)

                self.vboxes[vboxind].addWidget(cb)
                index += 1

        for vv, vbox in enumerate(self.vboxes):
            vbox.addStretch(1)

    def deleteFitCheckBoxes(self):
        """
        Delete all the checkboxes from the Widget. Used when a new pulsar is loaded
        """
        for fcbox in self.vboxes:
            while fcbox.count():
                item = fcbox.takeAt(0)
                if isinstance(item, QtGui.QWidgetItem):
                    item.widget().deleteLater()
                elif isinstance(item, QtGui.QSpacerItem):
                    fcbox.removeItem(item)
                else:
                    fcbox.clearLayout(item.layout())
                    fcbox.removeItem(item)

        for fcbox in self.vboxes:
            self.hbox.removeItem(fcbox)

        self.vboxes = []

    def changedFitCheckBox(self):
        """
        This is the signal handler when a checkbox is changed. The changed checkbox
        value will be propagated back to the psr object through callbacks.
        """
        # Check who sent the signal
        sender = self.sender()
        parchanged = sender.text()

        # Whatevs, we can just as well re-scan all the CheckButtons, and re-do
        # the fit
        for fcbox in self.vboxes:
            items = (fcbox.itemAt(i) for i in range(fcbox.count()))
            for w in items:
                if isinstance(w, QtGui.QWidgetItem) and \
                        isinstance(w.widget(), QtGui.QCheckBox) and \
                        parchanged == w.widget().text():
                    self.boxChecked(parchanged, bool(w.widget().checkState()))
                    print("{0} set to {1}".format(parchanged, bool(w.widget().checkState())))

class PlkXYPlotWidget(QtGui.QWidget):
    """
    A widget that allows one to choose which quantities to plot against each other
    """
    def __init__(self, parent=None, **kwargs):
        super(PlkXYPlotWidget, self).__init__(parent, **kwargs)

        self.parent = parent

        # We are going to use a grid layout:
        self.grid = QtGui.QGridLayout()
        self.grid.setSpacing(10)

        self.xButtonGroup = QtGui.QButtonGroup(self)
        self.xButtonGroup.buttonClicked[int].connect(self.updateXPlot)
        self.yButtonGroup = QtGui.QButtonGroup(self)
        self.yButtonGroup.buttonClicked[int].connect(self.updateYPlot)

        self.xSelected = 0
        self.ySelected = 0

        # TODO: implement this:
        # Connect the 'buttonClicked' signal 'self.setLabel'
        # There are two overloads for 'buttonClicked' signal: QAbstractButton (button itself) or int (id)
        # Specific overload for the signal is selected via [QtGui.QAbstractButton]
        # Clicking any button in the QButtonGroup will send this signal with the button
        # self.buttonGroup.buttonClicked[QtGui.QAbstractButton].connect(self.setLabel)
        # def setLabel(self, button):

        # Use an empty base pulsar to obtain the labels
        self.xychoices = plot_labels

        self.setPlkXYPlotLayout()

    def setPlkXYPlotLayout(self):
        labellength = 3

        label = QtGui.QLabel(self)
        label.setText("")
        self.grid.addWidget(label, 0, 0, 1, labellength)
        label = QtGui.QLabel(self)
        label.setText("X")
        self.grid.addWidget(label, 0, 0+labellength, 1, 1)
        label = QtGui.QLabel(self)
        label.setText("Y")
        self.grid.addWidget(label, 0, 1+labellength, 1, 1)

        # Add all the xychoices
        for ii, choice in enumerate(self.xychoices):
            # The label of the choice
            label = QtGui.QLabel(self)
            label.setText(choice)
            self.grid.addWidget(label, 1+ii, 0, 1, labellength)

            # The X and Y radio buttons
            radio = QtGui.QRadioButton("")
            self.grid.addWidget(radio, 1+ii, labellength, 1, 1)
            if choice.lower() == 'mjd':
                radio.setChecked(True)
                self.xSelected = ii
            self.xButtonGroup.addButton(radio)
            self.xButtonGroup.setId(radio, ii)

            radio = QtGui.QRadioButton("")
            self.grid.addWidget(radio, 1+ii, 1+labellength, 1, 1)
            if choice.lower() == 'residuals':
                radio.setChecked(True)
                self.ySelected = ii
            self.yButtonGroup.addButton(radio)
            self.yButtonGroup.setId(radio, ii)

        self.setLayout(self.grid)

    def setCallbacks(self, updatePlot):
        """
        Set the callback functions
        """
        self.updatePlot = updatePlot

    def plotids(self):
        """
        Return the X,Y ids of the selected quantities
        """
        return self.xychoices[self.xSelected], self.xychoices[self.ySelected]

    def updateXPlot(self, newid):
        """
        The x-plot radio buttons have been updated
        """
        self.xSelected = newid
        self.updateChoice()

    def updateYPlot(self, newid):
        """
        The y-plot radio buttons have been updated
        """
        self.ySelected = newid
        self.updateChoice()

    def updateChoice(self):
        # updatePLot was the callback from the main widget
        if self.updatePlot is not None:
            self.updatePlot()

class PlkWidget(QtGui.QWidget):
    """
    The plk-emulator window.

    @param parent:      Parent window
    """

    def __init__(self, parent=None, **kwargs):
        super(PlkWidget, self).__init__(parent, **kwargs)

        self.pspsr = None         # PulsarSolver object
        self.history = []       # History of previous solution candidates
        self.h_ind = None       # History index
        self.parent = parent

        self.initPlk()
        self.initPlkLayout()
        self.showVisibleWidgets()

    def initPlk(self):
        self.setMinimumSize(650, 550)

        self.plkbox = QtGui.QVBoxLayout()                       # plkbox contains the whole plk widget
        self.xyplotbox = QtGui.QHBoxLayout()                    # plkbox contains the whole plk widget
        #self.fitboxesWidget = PlkFitboxesWidget(parent=self)    # Contains all the checkboxes
        #self.actionsWidget = PlkActionsWidget(parent=self)

        # We are creating the Figure here, so set the color scheme appropriately
        self.setColorScheme(True)

        # Create the mpl Figure and FigCanvas objects.
        # 5x4 inches, 100 dots-per-inch
        #
        self.plkDpi = 100
        self.plkFig = Figure((5.0, 4.0), dpi=self.plkDpi)
        self.plkCanvas = FigureCanvas(self.plkFig)
        self.plkCanvas.setParent(self)

        # Since we have only one plot, we can use add_axes
        # instead of add_subplot, but then the subplot
        # configuration tool in the navigation toolbar wouldn't
        # work.
        #
        self.plkAxes = self.plkFig.add_subplot(111)

        # Done creating the Figure. Restore color scheme to defaults
        self.setColorScheme(False)

        # Call-back functions for clicking and key-press.
        self.plkCanvas.mpl_connect('button_press_event', self.canvasClickEvent)
        self.plkCanvas.mpl_connect('key_press_event', self.canvasKeyEvent)

        # Create the navigation toolbar, tied to the canvas
        #
        #self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)

        # Draw an empty graph
        self.drawSomething()

        # Create the XY choice widget
        self.xyChoiceWidget = PlkXYPlotWidget(parent=self)

        # At startup, all the widgets are visible
        self.xyChoiceVisible = True
        self.fitboxVisible = False
        self.actionsVisible = False
        self.layoutMode = 0         # (0 = xy select, 1 = no xy select)

        # Setup the user interface
        self.init_user_interface()

    def init_user_interface(self):
        """Initialize the user interface (zoom, deletions)"""
        # NOTE: Zoom is a general zooming mask. A zoom in one view, can be a
        #       random selection in another.
        if self.pspsr is None:
            self.mask_zoom = None
        else:
            self.mask_zoom = np.ones(self.pspsr.nobs, dtype=np.bool)

        self.show_delete = False        # Whether we show deleted points
        self.mergepatch_lower = None    # Which obs coordinate we are merging
        self.mergepatch_higher = None   # Which obs coordinate we are merging
        self.key_count = 0              # Iteration of the interface
        self.pred_data = None           # Prediction data
        self.pred_realizations = None   # Prediction realizations
        self.pred_stay = 0              # How many more turns before deleting

    def setColorScheme(self, start=True):
        """
        Set the color scheme

        @param start:   When true, save the original scheme, and set to white
                        When False, restore the original scheme
        """
        # Obtain the Widget background color
        color = self.palette().color(QtGui.QPalette.Window)
        r, g, b = color.red(), color.green(), color.blue()
        rgbcolor = (r/255.0, g/255.0, b/255.0)

        if start:
            # Copy of 'white', because of bug in matplotlib that does not allow
            # deep copies of rcParams. Store values of matplotlib.rcParams
            self.orig_rcParams = copy.deepcopy(constants.mpl_rcParams_white)
            for key, value in self.orig_rcParams.iteritems():
                self.orig_rcParams[key] = matplotlib.rcParams[key]

            rcP = copy.deepcopy(constants.mpl_rcParams_white)
            rcP['axes.facecolor'] = rgbcolor
            rcP['figure.facecolor'] = rgbcolor
            rcP['figure.edgecolor'] = rgbcolor
            rcP['savefig.facecolor'] = rgbcolor
            rcP['savefig.edgecolor'] = rgbcolor

            for key, value in rcP.iteritems():
                matplotlib.rcParams[key] = value
        else:
            for key, value in constants.mpl_rcParams_black.iteritems():
                matplotlib.rcParams[key] = value

    def drawSomething(self):
        """
        When we don't have a pulsar yet, but we have to display something, just draw
        an empty figure
        """
        self.setColorScheme(True)
        self.plkAxes.clear()
        self.plkAxes.grid(True)
        self.plkAxes.set_xlabel('MJD')
        self.plkAxes.set_ylabel('Residual ($\mu$s)')
        self.plkCanvas.draw()
        self.setColorScheme(False)

    def setPulsar(self, pspsr, history):
        """
        We've got a new pulsar!
        """
        # Set the pysolvepulsar object
        self.pspsr = pspsr
        self.history = history

        # And set an initial candidate solution
        cand = psp.CandidateSolution()
        cand.set_solution(pspsr.get_prior_values(), *pspsr.get_start_solution())
        self.history.append(cand)
        self.h_ind = 0

        # Setup the user interface
        self.init_user_interface()

        # Update the fitting checkboxes
        # TODO: do not use _psr
        #self.fitboxesWidget.setCallbacks(self.fitboxChecked,
        #    psr._psr.pars(which='set'), psr._psr.pars(which='fit'), nofitboxpars)
        self.xyChoiceWidget.setCallbacks(self.updatePlot)
        #self.actionsWidget.setCallbacks(self.updatePlot, self.reFit)

        # Draw the residuals
        self.xyChoiceWidget.updateChoice()

        # This screws up the show/hide logistics
        #self.show()

    def get_new_parfilename(self):
        """Return a new parfile name"""
        return self.pspsr.name + time.strftime('%Y-%m-%d-%H.%M.%S.par')

    def fitboxChecked(self, parchanged, newstate):
        """
        When a fitbox is (un)checked, this callback function is called

        @param parchanged:  Which parameter has been (un)checked
        @param newstate:    The new state of the checkbox
        """
        #self.psr[parchanged].fit = newstate
        pass

    def reFit(self):
        """
        We need to re-do the fit for this pulsar
        """
        if not self.pspsr is None:
            # Remove the history from here up
            while len(self.history) > self.h_ind+1:
                self.history.pop()
            #self.history = self.history[:self.h_ind+1]

            newcand, loglik = self.pspsr.fit_constrained_iterative(
                    self.history[self.h_ind])
            self.history.append(newcand)
            self.h_ind += 1
            self.updatePlot()

    def delete_observation(self, ind):
        """Create a new candidate solution in which we delete this point"""
        # Remove the history from here up
        while len(self.history) > self.h_ind+1:
            self.history.pop()
        self.history = self.history[:self.h_ind+1]

        # Copy the candidate solution and delete the point
        newcand = psp.CandidateSolution(self.history[self.h_ind])
        newcand.delete_observation(ind)

        # Add the solution
        self.history.append(newcand)
        self.h_ind += 1

    def merge_patches(self, lower, higher):
        """Merge coherence patches, based on these indices

        Merge all coherence patches between these MJDs.
        """
        # Obtain the data, on this scale
        cand = self.history[self.h_ind]
        xid, yid = self.xyChoiceWidget.plotids()
        x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)

        # Find all observations between 'lower' and 'higher'
        inds = np.where(np.logical_and(x >= x[lower], x <= x[higher]))[0]

        if len(inds) == 0:
            # Nothing to merge
            # TODO: Display a warning message?
            return

        # Create a new candidate solution, and remove history after here
        while len(self.history) > self.h_ind+1:
            self.history.pop()
        #self.history = self.history[:self.h_ind+1]
        newcand = psp.CandidateSolution(self.history[self.h_ind])

        # Find all the patches that belong to these observations
        pinds = []
        for ind in inds:
            pind = newcand.get_patch_from_obsind(ind)
            if pind is not None:
                # This is not a deleted point
                pinds.append(pind)

        # We need a sorted list of unique indices
        upinds = np.unique(pinds)

        # We need the absolute pulse numbers, as determine
        pulse_numbers = self.pspsr.pulsenumbers(newcand)

        # Merge these patches
        for ii, upind in enumerate(upinds[1:]):
            # Indices of upinds change (all -1), after merger
            m1, m2 = upinds[0], upind-ii

            # Join, with no relative phase jump
            newcand.join_patches(m1, m2, pulse_numbers, rpnjump=0)

        # Add the solution
        self.history.append(newcand)
        self.h_ind += 1

    def get_plot_patch_regions(self, x, msk, cand):
        """Given a set of plotting data (x-axis), return patch ids

        Given a set of plotting data for the x-axis, return the patch ids for
        all plotted data that belong to a coherent patch. This allows plotting
        the coherent patches in all plotting scenarios

        :param x:
            The x-axis values to plot (any data)

        :param msk:
            Mask of which observations to plot

        :param cand:
            Candidate solution

        :return:
            List of list of lists, which regions to highlight
            (color, patch, indices)
            Indices of x[msk][isort]
        """
        pvals = np.ones_like(x) * -1

        for pp, patch in enumerate(cand.get_patches()):
            pvals[patch] = pp

        # Sort the selection of points
        isort = np.argsort(x[msk], kind='mergesort')
        xs = x[msk][isort]
        pvs = pvals[msk][isort]
        upvs = np.unique(pvs)

        #bucket_ind = [[[]]]       # color, patch, indices
        bucket_ind = []       # color, patch, indices
        for cc, up in enumerate(upvs):
            # Start a new color
            bucket_ind.append([[]])
            uinds = np.where(pvs == up)[0]

            bucket_ind[cc][-1].append(uinds[0])
            for jj in uinds[1:]:
                if jj == bucket_ind[cc][-1][-1] + 1:
                    # Next element, add to patch
                    bucket_ind[cc][-1].append(jj)
                else:
                    # Elements are skipped. Start new patch
                    bucket_ind[cc].append([jj])

        return bucket_ind

    def newFitParameters(self):
        """
        This function is called when we have new fitparameters

        TODO: callback not used right now
        """
        pass

    def initPlkLayout(self):
        """
        Initialise the basic layout of this plk emulator emulator
        """
        # Initialise the plk box
        #self.plkbox.addWidget(self.fitboxesWidget)

        self.xyplotbox.addWidget(self.xyChoiceWidget)
        self.xyplotbox.addWidget(self.plkCanvas)

        self.plkbox.addLayout(self.xyplotbox)

        #self.plkbox.addWidget(self.actionsWidget)
        self.setLayout(self.plkbox)

    def showVisibleWidgets(self):
        """
        Show the correct widgets in the plk Window
        """
        self.xyChoiceWidget.setVisible(self.xyChoiceVisible)
        #self.fitboxesWidget.setVisible(self.fitboxVisible)
        #self.actionsWidget.setVisible(self.actionsVisible)

    def get_plotting_mask(self, cand):
        """Return a mask we use for plotting"""
        mask_delete = np.ones(len(self.mask_zoom), dtype=np.bool)
        mask_delete[cand.obs_inds] = False
        return self.mask_zoom.copy() if self.show_delete else \
                np.logical_and(self.mask_zoom, np.logical_not(mask_delete))

    def get_screen_limits(self, x, y, yerr=None):
        """Return the screen limits, based on the plotting data"""
        xave = 0.5 * (np.max(x) + np.min(x))
        xmin = xave - plotrangestretch * (xave - np.min(x))
        xmax = xave + plotrangestretch * (np.max(x) - xave)
        if yerr is None:
            yave = 0.5 * (np.max(y) + np.min(y))
            ymin = yave - plotrangestretch * (yave - np.min(y))
            ymax = yave + plotrangestretch * (np.max(y) - yave)
        else:
            yave = 0.5 * (np.max(y+yerr) + np.min(y-yerr))
            ymin = yave - plotrangestretch * (yave - np.min(y-yerr))
            ymax = yave + plotrangestretch * (np.max(y+yerr) - yave)

        return (xmin, xmax, ymin, ymax)

    def updatePlot(self):
        """
        Update the plot/figure
        """
        self.setColorScheme(True)
        self.plkAxes.clear()
        self.plkAxes.grid(True)

        if self.pspsr is not None:
            # The candidate solution we are plotting now
            cand = self.history[self.h_ind]

            # Get a mask for the plotting points
            msk = self.get_plotting_mask(cand)

            # Get the IDs of the X and Y axis
            xid, yid = self.xyChoiceWidget.plotids()

            # Retrieve the data
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)
            y, yerr, ylabel = self.pspsr.data_from_label(yid, cand=cand)

            if x is not None and y is not None and np.sum(msk) > 0:
                xp = x[msk]
                yp = y[msk]

                if yerr is not None:
                    yerrp = yerr[msk]
                else:
                    yerrp = None

                self.plotResiduals(xp, yp, yerrp, xlabel, ylabel, self.pspsr.name)
                self.plotCoherentPatches(x, msk, yp, yerrp, cand)
                if self.pred_data is not None and \
                        xid == 'mjd' and yid in ['Residuals', 'pulse phase']:
                    #self.plotPrediction(*self.pred_data)
                    F0 = self.pspsr._psr['F0'].val
                    bound, mult = (1.0/F0, 1.0) if yid == 'Residuals' else (1.0, F0)

                    self.plotPredictionRealizations(yp*mult, self.pred_realizations,
                            bound, *self.pred_data)

                    self.pred_stay -= 1
                    if self.pred_stay == 0:
                        self.pred_data = None
                        self.pred_realizations = None
            else:
                raise ValueError("Nothing to plot!")

        self.plkCanvas.draw()
        self.setColorScheme(False)

    def plotCoherentPatches(self, x, msk, y, yerr, cand):
        """Plot the coherence patches as colored regions

        Plot the coherence patches as colored regions.

        :param x:
            All the x-axis values

        :param msk:
            Mask of x-values, which we will plot

        :param cand:
            Candidate solution
        """
        bucket = self.get_plot_patch_regions(x, msk, cand)
        isort = np.argsort(x[msk], kind='mergesort')
        xs = x[msk][isort]

        # Screen limits
        xmin, xmax, ymin, ymax = self.get_screen_limits(x[msk], y, yerr)

        colors = ['b', 'r', 'g', 'c', 'y', 'k']
        ncols = len(colors)

        ncol = 0
        for bucket_col in [x for x in bucket if len(x) > 1 or len(x[0]) > 1]:
            # Have a patch with more than one element, or multiple patches
            for patch in bucket_col:
                if patch[0] == 0: #x[patch[0]] == np.min(x[msk]):
                    # Left-val edge of screen
                    x_left = xmin
                else:
                    x_left = 0.5 * (xs[patch[0]] + xs[patch[0]-1])

                if patch[-1] == len(xs)-1:#x[patch[-1]] == np.max(x[msk]):
                    # Rigth-val edge of screen
                    x_right = xmax
                else:
                    x_right = 0.5 * (xs[patch[-1]] + xs[patch[-1]+1])

                # Draw the patch
                self.plkAxes.fill_between([x_left, x_right], ymin, ymax, \
                        facecolor=colors[ncol % ncols], alpha=0.1)

            ncol += 1

    def plotResiduals(self, x, y, yerr, xlabel, ylabel, title):
        """
        Update the plot, given all the plotting info
        """
        xmin, xmax, ymin, ymax = self.get_screen_limits(x, y, yerr)

        if yerr is None:
            self.plkAxes.scatter(x, y, marker='.', color='blue')
        else:
            self.plkAxes.errorbar(x, y, yerr=yerr, fmt='.', color='blue')

        self.plkAxes.axis([xmin, xmax, ymin, ymax])
        self.plkAxes.get_xaxis().get_major_formatter().set_useOffset(False)
        self.plkAxes.set_xlabel(xlabel)
        self.plkAxes.set_ylabel(ylabel)
        self.plkAxes.set_title(title, y=1.03)

    def plotPrediction(self, t, dt_r, dt_stdrp):
        P0 = 1.0 / self.pspsr._psr['F0'].val
        self.plkAxes.plot(t, dt_r, 'k--', linewidth=2.0)
        rmin, rmax = dt_r-dt_stdrp, dt_r+dt_stdrp
        rmin[rmin < -0.5*P0] = -0.5*P0
        rmax[rmin > 0.5*P0] = 0.5*P0
        self.plkAxes.fill_between(t, rmin, rmax, facecolor='k', alpha=0.3)

    def plotPredictionRealizations(self, yp, mr, bound, t, dt_r, dt_stdrp):
        #P0 = 1.0 / self.pspsr._psr['F0'].val
        self.plkAxes.plot(t, dt_r, 'k--', linewidth=2.0)
        rmin, rmax = dt_r-dt_stdrp, dt_r+dt_stdrp
        pmin, pmax = min(-0.5*bound, np.min(yp)), max(0.5*bound, np.max(yp))
        rmin[rmin < pmin] = pmin
        rmax[rmin > pmax] = pmax
        self.plkAxes.plot(t, rmin, 'k-', linewidth=1.2)
        self.plkAxes.plot(t, rmax, 'k-', linewidth=1.2)
        self.plkAxes.fill_between(t, rmin, rmax, facecolor='k', alpha=0.1)
        for ii in range(mr.shape[1]):
            r = mr[:,ii]
            r[r < -0.5*bound] = -0.5*bound
            r[r > 0.5*bound] = 0.5*bound
            self.plkAxes.plot(t, (dt_r+r), c='darkred', linestyle='-',
                    linewidth=0.5, alpha=0.3)

        #self.plkAxes.plot(t, dt_r, 'k--', linewidth=2.0)
        #rmin, rmax = dt_r-dt_stdrp, dt_r+dt_stdrp
        #rmin[rmin < -0.5*bound] = -0.5*bound
        #rmax[rmin > 0.5*bound] = 0.5*bound
        #self.plkAxes.fill_between(t, rmin, rmax, facecolor='k', alpha=0.3)

    def plotPhaseJumps_deprecated(self, phasejumps):
        """
        Plot the phase jump lines, if we have any
        """
        xmin, xmax, ymin, ymax = self.plkAxes.axis()
        dy = 0.01 * (ymax-ymin)

        if len(phasejumps) > 0:
            phasejumps = np.array(phasejumps)

            for ii in range(len(phasejumps)):
                if phasejumps[ii,1] != 0:
                    self.plkAxes.vlines(phasejumps[ii,0], ymin, ymax,
                            color='darkred', linestyle='--', linewidth=0.5)

                    if phasejumps[ii,1] < 0:
                        jstr = str(phasejumps[ii,1])
                    else:
                        jstr = '+' + str(phasejumps[ii,1])

                    # Print the jump size above the plot
                    ann = self.plkAxes.annotate(jstr, \
                            xy=(phasejumps[ii,0], ymax+dy), xycoords='data', \
                            annotation_clip=False, color='darkred', \
                            size=7.0)

    def setFocusToCanvas(self):
        """
        Set the focus to the plk Canvas
        """
        self.plkCanvas.setFocus()

    def coord2point(self, cx, cy, which='xy', mode='both'):
        """
        Given data coordinates x and y, obtain the index of the observations
        that is closest to it

        @param cx:      x-value of the coordinates
        @param cy:      y-value of the coordinates
        @param which:   which axis to include in distance measure [xy/x/y]
        @param mode:    From which side to include (both/lower/higher)
                        (used when which='x' or which='y'

        @return:    Index of observation
        """
        ind = None

        if self.pspsr is not None:
            # The candidate solution we are plotting now
            cand = self.history[self.h_ind]

            # Get a mask for the plotting points
            msk = self.get_plotting_mask(cand)

            # Get the IDs of the X and Y axis
            xid, yid = self.xyChoiceWidget.plotids()

            # Retrieve the data
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)
            y, yerr, ylabel = self.pspsr.data_from_label(yid, cand=cand)

            if np.sum(msk) > 0 and x is not None and y is not None:
                # Obtain the limits
                xmin, xmax, ymin, ymax = self.plkAxes.axis()

                if which == 'xy':
                    dist = ((x[msk]-cx)/(xmax-xmin))**2 + ((y[msk]-cy)/(ymax-ymin))**2
                elif which == 'x' and mode == 'both':
                    dist = ((x[msk]-cx)/(xmax-xmin))**2
                elif which == 'y' and mode == 'both':
                    dist = ((y[msk]-cy)/(ymax-ymin))**2
                elif which == 'x' and mode == 'lower':
                    dist = ((x[msk]-cx)/(xmax-xmin))**2
                    dist[x[msk]-cx >= 0] = np.inf
                    if np.all(dist == np.inf):
                        dist[np.argmin(x[msk])] = 0.0
                elif which == 'x' and mode == 'higher':
                    dist = ((x[msk]-cx)/(xmax-xmin))**2
                    dist[x[msk]-cx <= 0] = np.inf
                    if np.all(dist == np.inf):
                        dist[np.argmax(x[msk])] = 0.0
                elif which == 'y' and mode == 'lower':
                    dist = ((y[msk]-cy)/(ymax-ymin))**2
                    dist[y[msk]-cy >= 0] = np.inf
                    if np.all(dist == np.inf):
                        dist[np.argmin(x[msk])] = 0.0
                elif which == 'y' and mode == 'higher':
                    dist = ((y[msk]-cy)/(ymax-ymin))**2
                    dist[y[msk]-cy <= 0] = np.inf
                    if np.all(dist == np.inf):
                        dist[np.argmax(x[msk])] = 0.0
                else:
                    raise ValueError("Value {0} not a valid option for coord2point".format(which))

                ind = np.arange(len(x))[msk][np.argmin(dist)]

        return ind

    def keyPressEvent(self, event, **kwargs):
        """
        A key is pressed. Handle all the shortcuts here.

        This function can be called as a callback from the Canvas, or as a
        callback from Qt. So first some parsing must be done
        """
        if hasattr(event.key, '__call__'):
            ukey = event.key()
            modifiers = int(event.modifiers())
            from_canvas = False

            print("WARNING: call-back key-press, canvas location not available")

            xpos, ypos = None, None
        else:
            # Modifiers are noted as: key = 'ctrl+alt+F', or 'alt+control', or
            # 'shift+g'. Do some parsing
            fkey = event.key
            from_canvas = True

            xpos, ypos = event.xdata, event.ydata

            ukey = ord(fkey[-1])
            modifiers = QtCore.Qt.NoModifier
            if 'ctrl' in fkey:
                modifiers += QtCore.Qt.ControlModifier
            if 'shift' in fkey:
                modifiers += QtCore.Qt.ShiftModifier
            if 'alt' in fkey:
                modifiers += QtCore.Qt.AltModifier
            if 'meta' in fkey:
                modifiers += QtCore.Qt.MetaModifier
            if 'backspace' in fkey:
                ukey = QtCore.Qt.Key_Backspace

        #if int(e.modifiers()) == (QtCore.Qt.ControlModifier+QtCore.Qt.AltModifier)

        if ukey == QtCore.Qt.Key_Escape:
            if self.parent is None:
                self.close()
            else:
                self.parent.close()
        if (ukey == ord('z') or ukey == ord('Z')) and \
                (modifiers == QtCore.Qt.ControlModifier or \
                modifiers == QtCore.Qt.MetaModifier):
            # TODO: How do I really catch the MetaModifier? On OSX, I can't get
            #       the command key
            if self.h_ind > 0:
                self.h_ind -= 1

            self.updatePlot()
        if (ukey == ord('r') or ukey == ord('R')) and \
                (modifiers == QtCore.Qt.ControlModifier or \
                modifiers == QtCore.Qt.MetaModifier):
            # TODO: How do I really catch the MetaModifier? On OSX, I can't get
            #       the command key
            if self.h_ind + 1 < len(self.history):
                self.h_ind += 1

            self.updatePlot()
        elif (ukey == ord('M') or ukey == ord('m')) and \
                modifiers == QtCore.Qt.ControlModifier:
                #(modifiers == QtCore.Qt.ControlModifier or \
                #modifiers == QtCore.Qt.MetaModifier):
            # Change the window
            self.layoutMode = (1+self.layoutMode)%2
            if self.layoutMode == 0:
                self.xyChoiceVisible = True
                self.fitboxVisible = False
                self.actionsVisible = False
            elif self.layoutMode == 1:
                self.xyChoiceVisible = False
                self.fitboxVisible = False
                self.actionsVisible = False
            self.showVisibleWidgets()
        elif ukey == ord('s'):
            # Get the x-value data
            cand = self.history[self.h_ind]
            xid, yid = self.xyChoiceWidget.plotids()
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)

            # Get the index of the start-point
            ind = self.coord2point(xpos, ypos, which='x', mode='higher')

            # Mask everything before it (not the point itself)
            self.mask_zoom[x < x[ind]] = False

            self.updatePlot()
        elif ukey == ord('f'):
            # Get the x-value data
            cand = self.history[self.h_ind]
            xid, yid = self.xyChoiceWidget.plotids()
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)

            # Get the index of the start-point
            ind = self.coord2point(xpos, ypos, which='x', mode='lower')

            # Mask everything after it (not the point itself)
            self.mask_zoom[x > x[ind]] = False

            self.updatePlot()
        elif ukey == ord('u'):
            # Unzoom
            self.mask_zoom[:] = True
            self.updatePlot()
        elif ukey == ord('d'):
            # Delete data point
            ind = self.coord2point(xpos, ypos)
            self.delete_observation(ind)

            self.updatePlot()
        elif ukey == ord('['):
            # Start of a coherence-patch merge
            # Get the x-value data
            cand = self.history[self.h_ind]
            xid, yid = self.xyChoiceWidget.plotids()
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)

            # Get the index of the start-point
            ind = self.coord2point(xpos, ypos, which='x', mode='higher')

            if self.mergepatch_higher is not None:
                self.merge_patches(ind, self.mergepatch_higher)

                # Reset the memory
                self.mergepatch_lower = None
                self.mergepatch_higher = None
            else:
                self.mergepatch_lower = ind

            self.updatePlot()
        elif ukey == ord(']'):
            # End of a coherence-patch merge
            # Get the x-value data
            cand = self.history[self.h_ind]
            xid, yid = self.xyChoiceWidget.plotids()
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)

            # Get the index of the start-point
            ind = self.coord2point(xpos, ypos, which='x', mode='lower')

            if self.mergepatch_lower is not None:
                self.merge_patches(self.mergepatch_lower, ind)

                # Reset the memory
                self.mergepatch_lower = None
                self.mergepatch_higher = None
            else:
                self.mergepatch_higher = ind

            self.updatePlot()
        elif ukey == ord('c') or ukey == ord('C'):
            # Split a coherence patch at this point
            while len(self.history) < self.h_ind+1:
                self.history.pop()
            #self.history = self.history[:self.h_ind+1]

            newcand = psp.CandidateSolution(self.history[self.h_ind])
            newcand = self.history[self.h_ind]
            ind = self.coord2point(xpos, ypos, which='x', mode='higher')
            xid, yid = self.xyChoiceWidget.plotids()
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=newcand)
            pind = newcand.get_patch_from_obsind(ind)
            if pind is not None:
                patch = newcand._patches[pind]
                msk = (x[patch] >= x[ind])
                newcand.split_patch(pind, msk)

                self.history.append(newcand)
                self.h_ind += 1

            self.updatePlot()
        elif ukey == ord('e') or ukey == ord('E'):
            # Do the extrapolation
            # First obtain the plotting ranges etc.
            cand = self.history[self.h_ind]
            xid, yid = self.xyChoiceWidget.plotids()
            if xid in ['mjd'] and yid in ['Residuals', 'pulse phase']:
                # Get the screen layout
                x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=cand)
                y, yerr, ylabel = self.pspsr.data_from_label(yid, cand=cand)
                msk = self.get_plotting_mask(cand)
                xmin, xmax, ymin, ymax = self.get_screen_limits(x[msk],
                        y[msk], yerr[msk])

                # Obtain the patch from which we'll extrapolate
                ind = self.coord2point(xpos, ypos, which='xy')
                pind = cand.get_patch_from_obsind(ind)
                if pind is not None:
                    # Perform the linear least-squares extrapolation
                    dd = self.pspsr.perform_linear_least_squares_fit_old(cand,
                            fitpatch=pind)

                    mult = 1.0 if yid == 'Residuals' else \
                           self.pspsr._psr['F0'].val

                    npobs = self.pspsr.predpsr_nobs
                    obstimes = np.linspace(xmin, xmax, npobs)
                    dt_r, dt_stdrp = self.pspsr.get_mock_prediction(obstimes, dd)
                    self.pred_data = (obstimes, dt_r*mult, dt_stdrp*mult)
                    self.pred_realizations = self.pspsr.get_mock_realizations(\
                            obstimes, dd)[1] * mult
                    self.pred_stay = 2

            self.updatePlot()
        elif ukey == ord('+') or ukey == ord('-'):
            # Add/delete a phase jump
            rpnjump = 1 if ukey == ord('-') else -1

            while len(self.history) > self.h_ind+1:
                self.history.pop()
            #self.history = self.history[:self.h_ind+1]

            newcand = psp.CandidateSolution(self.history[self.h_ind])
            #newcand = self.history[self.h_ind]
            ind = self.coord2point(xpos, ypos, which='x', mode='higher')
            xid, yid = self.xyChoiceWidget.plotids()
            x, xerr, xlabel = self.pspsr.data_from_label(xid, cand=newcand)
            pind = newcand.get_patch_from_obsind(ind)
            if pind is not None:
                patch = newcand._patches[pind]
                msk = (x[patch] >= x[ind])

                newcand.add_rpn_jump(pind, msk, rpnjump)

                self.history.append(newcand)
                self.h_ind += 1

            self.updatePlot()
            """
            jump = 1
            if ukey == ord('-'):
                jump = -1

            ind = self.coord2point(xpos, ypos, which='x')
            self.psr.add_phasejump(self.psr.stoas[ind], jump)
            self.updatePlot()
            """
        elif ukey == QtCore.Qt.Key_Backspace:
            # Remove all phase jumps
            # Not necessary anymore
            pass
        elif ukey == ord('P'):
            # Write a new parfile
            parfilename = self.get_new_parfilename()
            cand = self.history[self.h_ind]

            dd = self.pspsr.perform_linear_least_squares_fit_old(cand)
            self.pspsr.savepar(parfilename, dd)
        elif ukey == ord('<'):
            """
            # Add a data point to the view on the left
            # TODO: Make this more Pythonic!
            if self.psr['START'].set and self.psr['START'].fit:
                start = self.psr['START'].val
                ltmask = self.psr.stoas < start

                if np.sum(ltmask) > 2:
                    ltind = np.arange(len(self.psr.stoas))[ltmask]
                    lttoas = self.psr.stoas[ltmask]
                    max_ltind = np.argmax(lttoas)

                    # Get maximum of selected TOAs
                    ltmax = ltind[max_ltind]
                    start_max = self.psr.stoas[ltmax]

                    # Get second-highest TOA value
                    ltmask[ltmax] = False
                    ltind = np.arange(len(self.psr.stoas))[ltmask]
                    lttoas = self.psr.stoas[ltmask]
                    max_ltind = np.argmax(lttoas)
                    ltmax = ltind[max_ltind]
                    start_max2 = self.psr.stoas[ltmax]

                    # Set the new START value
                    self.psr['START'].val = 0.5 * (start_max + start_max2)
                elif np.sum(ltmask) == 2:
                    idmin = np.argmin(self.psr.stoas)
                    stmin = self.psr.stoas[idmin]
                    mask = np.ones(len(self.psr.stoas), dtype=np.bool)
                    mask[idmin] = False
                    self.psr['START'].val = 0.5 * \
                            (np.min(self.psr.stoas[mask]) + stmin)
                elif np.sum(ltmask) == 1:
                    self.psr['START'].val = np.min(self.psr.stoas) - 1
                elif np.sum(ltmask) == 0:
                    pass
            """
            self.updatePlot()
        elif ukey == ord('>'):
            """
            # Add a data point to the view on the left
            # TODO: Make this more Pythonic!
            if self.psr['FINISH'].set and self.psr['FINISH'].fit:
                start = self.psr['FINISH'].val
                gtmask = self.psr.stoas > start

                if np.sum(gtmask) > 2:
                    gtind = np.arange(len(self.psr.stoas))[gtmask]
                    gttoas = self.psr.stoas[gtmask]
                    min_gtind = np.argmin(gttoas)

                    # Get maximum of selected TOAs
                    gtmin = gtind[min_gtind]
                    start_min = self.psr.stoas[gtmin]

                    # Get second-highest TOA value
                    gtmask[gtmin] = False
                    gtind = np.arange(len(self.psr.stoas))[gtmask]
                    gttoas = self.psr.stoas[gtmask]
                    min_gtind = np.argmin(gttoas)
                    gtmin = gtind[min_gtind]
                    start_min2 = self.psr.stoas[gtmin]

                    # Set the new FINISH value
                    self.psr['FINISH'].val = 0.5 * (start_min + start_min2)
                elif np.sum(gtmask) == 2:
                    idmax = np.argmax(self.psr.stoas)
                    stmax = self.psr.stoas[idmax]
                    mask = np.ones(len(self.psr.stoas), dtype=np.bool)
                    mask[idmax] = False
                    self.psr['FINISH'].val = 0.5 * \
                            (np.max(self.psr.stoas[mask]) + stmax)
                elif np.sum(gtmask) == 1:
                    self.psr['FINISH'].val = np.max(self.psr.stoas) + 1
                elif np.sum(gtmask) == 0:
                    pass
            """
            self.updatePlot()
        elif ukey == ord('x'):
            # Re-do the fit, using post-fit values of the parameters
            self.reFit()
        elif ukey == QtCore.Qt.Key_Left:
            # print("Left pressed")
            pass
        else:
            #print("Other key: {0} {1} {2} {3}".format(ukey,
            #    modifiers, ord('M'), QtCore.Qt.ControlModifier))
            pass

        #print("PlkWidget: key press: ", ukey, xpos, ypos)
        self.key_count += 1

        if not from_canvas:
            if self.parent is not None:
                print("Propagating key press")
                self.parent.keyPressEvent(event)

            super(PlkWidget, self).keyPressEvent(event, **kwargs)

    def canvasClickEvent(self, event):
        """
        When one clicks on the Figure/Canvas, this function is called. The
        coordinates of the click are stored in event.xdata, event.ydata
        """
        #print('Canvas click, you pressed', event.button, event.xdata, event.ydata)
        pass

    def canvasKeyEvent(self, event):
        """
        When one presses a button on the Figure/Canvas, this function is called.
        The coordinates of the click are stored in event.xdata, event.ydata
        """
        # Callback to the plkWidget
        self.keyPressEvent(event)


import pygame
from entities import *
from settings import *
from tiles import *
import os
import math

class Level:
	def __init__(self, level_data, surface):
		self.display_surface = surface
		self.setup_level(level_data)
		self.world_shift = [0, 0]

		self.current_x = 0

		self.desired_screen_position = [screen_width/2, screen_height/2]

	def setup_level(self, map):
		self.tiles = pygame.sprite.Group()
		self.player = pygame.sprite.GroupSingle()

		for row_index, row in enumerate(map):
			for col_index, cell in enumerate(row):
				x = col_index * tile_size
				y = row_index * tile_size

				if cell == "X":
					tile = Tile((x, y), tile_size)
					self.tiles.add(tile)

				if cell == "P":
					player = Player((x, y))
					self.player.add(player)

	def scroll_x(self):
		player = self.player.sprite
		player_centerx = player.rect.centerx

		relative_position_x = (player_centerx - self.desired_screen_position[0])/20

		if relative_position_x < 0:
			player.rect.centerx -= math.floor(relative_position_x)
			self.world_shift[0] = math.floor(relative_position_x)
		else:
			player.rect.centerx -= math.ceil(relative_position_x)
			self.world_shift[0] = math.ceil(relative_position_x)

	def scroll_y(self):
		player = self.player.sprite

		relative_position_y = (player.rect.centery -
							   self.desired_screen_position[1])/10

		if relative_position_y < 0:
			player.rect.centery -= math.floor(relative_position_y)
			self.world_shift[1] = math.floor(relative_position_y)

		else:
			player.rect.centery -= math.ceil(relative_position_y)
			self.world_shift[1] = math.ceil(relative_position_y)

	def get_collisions(self, rect):
		collisions = []
		for tile in self.tiles.sprites():
			if tile.rect.colliderect(rect):
				collisions.append(tile)
		return collisions

	def x_collisions(self):
		player = self.player.sprite

		player.rect.centerx += player.movement[0] * player.vel

		collisions = self.get_collisions(player.rect)

		for tile in collisions:
			if player.movement[0] > 0:
				player.rect.right = tile.rect.left
			elif player.movement[0] < 0:
				player.rect.left = tile.rect.right

	def y_collisions(self):
		player = self.player.sprite

		player.rect.centery += player.movement[2] * player.vel

		collisions = self.get_collisions(player.rect)

		for tile in collisions:

			if player.movement[2] > 0:
				player.rect.bottom = tile.rect.top

			elif player.movement[2] < 0:
				player.rect.top = tile.rect.bottom

	def run(self):

		self.player.update()

		self.x_collisions()
		self.scroll_x()
		self.tiles.update(x=self.world_shift[0])

		self.y_collisions()
		self.scroll_y()
		self.tiles.update(y=self.world_shift[1])

		self.tiles.draw(self.display_surface)
		self.player.draw(self.display_surface)

if __name__ == "__main__":
	os.system("python main.py")


from recipe_engine.post_process import DoesNotRun, Filter, StatusFailure

DEPS = [
    'flutter/build_util',
    'fuchsia/goma',
    'recipe_engine/context',
    'recipe_engine/path',
    'recipe_engine/properties',
]

def RunSteps(api):
  checkout = api.path['start_dir']
  api.goma.ensure()
  env = {'GOMA_DIR': api.goma.goma_dir}
  env_prefixes = {}
  with api.context(env=env, env_prefixes=env_prefixes):
    api.build_util.run_gn([], checkout)
    api.build_util.build('release', checkout, ['mytarget'])

def GenTests(api):
  yield api.test('basic', api.properties(no_lto=True, goma_jobs=100))


# html5 says these elements should not have a closing tags (or empty tag)
EMPTY_ELEMENTS = {'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
                 'link', 'meta', 'param', 'source', 'track', 'wbr'}

class Element:
    '''
    An HTML element. Has a tag, optionally attributes and optionally child tags
    '''
    def __init__(self, tagname, attrs=None, content=None):
        '''
        tagname = type of this tag
        attrs = list of other attribute tuples
        content = string that will be added as a TextNode (for one line
            Element creation)
        '''
        self.tagname = tagname
        self.attr = attrs if attrs is not None else []
        self.children = []
        if content is not None:
            self.append(TextNode(content))

    def append(self, child):
        '''
        child = childnode to add to this node

        returns child for storing children that are created directly in the
            arguments
        '''
        if self.tagname.lower() in EMPTY_ELEMENTS:
            raise Exception('Content not permitted on Empty elements')
        self.children.append(child)
        return child

    def render(self, depth=0, sep='  '):
        '''
        depth = depth for indentation (starting at 0 for no indentation
        sep = separator for indention, will be multiplied by the depth
        '''
        dest = []
        indent = sep * depth
        tag = self.tagname
        attrs = []
        for a in self.attr:
            attrs.append(f'{a[0]}="{a[1]}"')
        attrstr = ' '.join(attrs)
        if attrstr:
            attrstr = ' ' + attrstr

        if not self.children:
            if self.tagname.lower() in EMPTY_ELEMENTS:
                closing = '>'
            else:
                closing = ' />'
            dest.append(f'{indent}<{tag}{attrstr}{closing}')
        elif (len(self.children) == 1 and
              isinstance(self.children[0], TextNode)):
            # only 1 text node child, do it all inline
            contents = self.children[0].contents
            dest.append(f'{indent}<{tag}{attrstr}>{contents}</{tag}>')
        else:
            # has children
            dest.append(f'{indent}<{tag}{attrstr}>')
            for c in self.children:
                dest.extend(c.render(depth + 1, sep))

            dest.append(f'{indent}</{tag}>')

        return dest

class TextNode:
    def __init__(self, contents):
        '''
        contents = exact contents of the body of this element

        Can also be used for full html elements, but wont be able to search in
        them etc.
        '''
        self.contents = contents

    def render(self, depth=0, sep='  '):
        return [(sep * depth) + self.contents]

class Page:
    def __init__(self, title='', faviconpath=''):
        '''
        '''
        c = []
        self.children = c
        c.append(TextNode("<!DOCTYPE html>"))
        html = Element("html", [('lang', 'en')])
        c.append(html)
        head = html.append(Element("head"))
        head.append(Element('meta', [('charset', 'utf-8')]))
        head.append(Element('meta', [('google', 'notranslate')]))
        head.append(Element('meta', [('http-equiv', 'Content-Language'),
                                     ('content', 'en_US')]))
        head.append(Element('meta', [('http-equiv', 'X-UA-Compatible'),
                                     ('content', 'IE=edge')]))
        head.append(Element('meta', [('name', 'viewport'),
                                     ('content',
                                      'width-device-width, initial-scale=1')]))
        title = head.append(Element('title', content=title))
        if faviconpath:
            head.append(Element('link', [('rel', 'icon'),
                                         ('href', faviconpath),
                                         ('type', 'image/x-icon')]))
            head.append(Element('link', [('rel', 'shortcut icon'),
                                         ('href', faviconpath),
                                         ('type', 'image/x-icon')]))
        body = html.append(Element("body"))

        self.head = head
        self.body = body

    def __str__(self):
        dest = []
        for c in self.children:
            dest.extend(c.render())
        return '\n'.join(dest)


# -*- coding: utf-8 -*-
"""
Created on Thu Apr  4 09:11:12 2019

@author: Sushant
"""

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

dataset = pd.read_csv('bank-additional-full.csv',sep=";")

non_catagorical_values = ["age", "campaign", "pdays", "previous", "duration", "emp.var.rate", "cons.price.idx", "cons.conf.idx", "euribor3m", "nr.employed"]
categorical_values=["job","marital","education","default","housing","loan","contact","month","day_of_week","poutcome"]

from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder()
encoder.fit(dataset[categorical_values])
temp=encoder.transform(dataset[categorical_values]).toarray()

encoder_column=[str(i) for i in range(temp.shape[1])]
for value,i in enumerate(encoder_column):
    dataset[i]=temp[:,value]

dataset=dataset.drop(categorical_values,axis=1)

dataset["y"]=[1 if i=="yes" else 0 for i in dataset["y"]]
X = dataset.iloc[: , :-1].values
y = dataset.iloc[: , -1].values

from sklearn.model_selection import train_test_split
X_train,X_test,Y_train,Y_test = train_test_split(X ,y ,test_size = 0.3,random_state = 0 ,  stratify = y)

from sklearn.preprocessing import StandardScaler
sc_X =  StandardScaler()
X_train = sc_X.fit_transform(X_train)
X_test = sc_X.transform(X_test)

from sklearn.svm import SVC
classifier = SVC(kernel = 'linear', random_state = 0)
classifier.fit(X_train,Y_train)
y_pred = classifier.predict(X_test)

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(Y_test, y_pred)

from sklearn.metrics import precision_score, recall_score, f1_score, accuracy_score
print('Precision Score: ', precision_score(Y_test, y_pred))
print('Recall Score: ', recall_score(Y_test, y_pred))
print('f1 Score: ', f1_score(Y_test, y_pred))
print('accuracy Score: ', accuracy_score(Y_test, y_pred))


import time
from turtle import Turtle, Screen
from pong import Pong
from ball import Ball
from scoreboard import Scoreboard
screen = Screen()

screen.setup(width=800, height=600)
screen.bgcolor("black")
screen.title("My Pong Game")
screen.tracer(0)

r_pong = Pong((350, 0))
l_pong = Pong((-350, 0))
ball = Ball()
scoreboard = Scoreboard()

screen.listen()
screen.onkey(r_pong.go_up, "Up")
screen.onkey(r_pong.go_down, "Down")
screen.onkey(l_pong.go_up, "w")
screen.onkey(l_pong.go_down, "s")

game_is_on = True
while game_is_on:
    screen.update()
    time.sleep(ball.move_speed)
    ball.move()

    # Detect collision with wall
    if ball.ycor() > 280 or ball.ycor() < -280:
        ball.bounce_y()

    # Detect collision with paddle
    if ball.distance(r_pong) < 50 and ball.xcor() > 320 or ball.distance(l_pong) < 50 and ball.xcor() < -320:
        ball.bounce_x()

    if ball.xcor() > 380:
        ball.reset_position()
        scoreboard.l_point()

    if ball.xcor() < -380:
        ball.reset_position()
        scoreboard.r_point()

screen.exitonclick()


print('importing')

# imports
from tensorflow.keras.models import load_model
import numpy as np
import cv2

# transform opencv image, size=(height, width)
def fit_image(img, size=(224, 224), cvt_color=cv2.COLOR_BGR2RGB):
  scale_percent = max(size[0] / img.shape[0], size[1] / img.shape[1])
  width = int(img.shape[1] * scale_percent)
  height = int(img.shape[0] * scale_percent)

  # convert color and resize image
  if cvt_color != None:
    img = cv2.cvtColor(img, cvt_color)
  scale_img = cv2.resize(img, (width, height), interpolation=cv2.INTER_NEAREST)

  # crop from center of image
  center = (int(scale_img.shape[0]/2), int(scale_img.shape[1]/2))
  half_size = (int(size[0]/2), int(size[1]/2))
  crop_img = scale_img[center[0]-half_size[0]:center[0]+half_size[0], center[1]-half_size[1]:center[1]+half_size[1]]
  return crop_img

# preprocess opencv image for classification
def preprocess_image(image):
  # transform image
  image = fit_image(image)
  image_array = np.asarray(image)
  normalized_image_array = (image_array.astype(np.float32) / 127.0) - 1 # normalize image

  # reshape to fit the model
  data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)
  data[0] = normalized_image_array
  return data

print('now loading model')
model = load_model('keras_model.h5')
print('finished loading model')

def predict(frame):

  labels = {
    0: 'none',
    1: 'trash',
    2: 'recycle',
    3: 'compost'
  }

  # predict the class
  frame = fit_image(frame, cvt_color=None)
  inp = preprocess_image(frame)
  prediction = model.predict(inp)
  result = np.argmax(prediction)

  return labels[result]

'''
if __name__=="__main__":
  cap = cv2.VideoCapture(0)

  while True:

    ret, frame = cap.read()

    if not ret:
      continue

    result = predict(inp)

    print(result)

    if cv2.waitKey(1) & 0xFF == ord('q'):
      break

  cap.release()
  '''


import os
from django.utils import timezone
from reportlab.lib import colors
from reportlab.lib.colors import HexColor
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT, TA_RIGHT
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.pagesizes import letter, landscape
from django.template.defaultfilters import date as _date
from django.contrib.humanize.templatetags.humanize import intcomma
from reportlab.lib.units import mm
from reportlab.platypus import Paragraph, Spacer, Table, TableStyle, Image
from reportlab.pdfbase.pdfmetrics import stringWidth
from django.utils.translation import gettext_lazy as _
from django.contrib.humanize.templatetags.humanize import intcomma

from index.reportbase import BaseFormatReport

TITLE1 = ParagraphStyle(
    name='Title1',
    textTransform='uppercase',
    fontName='tahoma_bold',
    fontSize=11,
    leading=15,
    alignment=TA_CENTER,
    spaceAfter=0,
    spaceShrinkage=8
)

TEXT_LEFT_TABLE = ParagraphStyle(
    name='TextRightTable',
    fontName='tahoma',
    fontSize=9,
    leading=12,
    spaceBefore=0,
    alignment=TA_LEFT
)

TEXT_RIGHT_TABLE = ParagraphStyle(
    name='TextRightTable',
    fontName='tahoma',
    fontSize=9,
    leading=12,
    spaceBefore=0,
    alignment=TA_RIGHT
)
TEXT_RIGHT_TABLE_BOLD = ParagraphStyle(
    name='TextRightTable',
    fontName='tahoma_bold',
    fontSize=8,
    leading=12,
    spaceBefore=0,
    alignment=TA_RIGHT
)
TEXT_LEFT_TABLE_BOLD = ParagraphStyle(
    name='TextRightTable',
    fontName='tahoma_bold',
    fontSize=8,
    leading=12,
    spaceBefore=0,
    alignment=TA_LEFT
)
TITLE_RIGHT_TABLE_BOLD = ParagraphStyle(
    name='TextRightTable',
    fontName='tahoma_bold',
    fontSize=8,
    leading=12,
    spaceBefore=0,
    alignment=TA_RIGHT
)
TITLE_CENTER_TABLE_BOLD = ParagraphStyle(
    name='TextRightTable',
    fontName='tahoma_bold',
    fontSize=13,
    leading=13,
    spaceBefore=10,
    alignment=TA_CENTER
)
TITLE_LEFT_TABLE_BOLD = ParagraphStyle(
    name='TextRightTable',
    fontName='tahoma_bold',
    fontSize=9,
    leading=12,
    spaceBefore=0,
    alignment=TA_LEFT
)

TEXT_CENTER_TABLE = ParagraphStyle(
    name='TextCenterTable',
    fontName='tahoma',
    fontSize=8,
    leading=12,
    spaceBefore=6,
    alignment=TA_CENTER
)

class ReservaPDF(BaseFormatReport):
    MAX_Y = 279*mm
    MAX_X = 216*mm
    CONTEXT = None

    def _file_name(self):
        name = u'reserva_cliente'
        return name

    def generate_pdf(self, build=True):
        self.CONTEXT = self.kwargs.get('reserva', None)
        if build:
            self.doc.filename = self._file_name()
        else:
            self.doc.title = self._file_name()

        self.doc.topMargin = 50*mm
        _onPage = self._page_header

        # self._table()
        self._body()
        self._body1()
        self._body2()
        self.doc.build(self.Story,
                    onFirstPage=_onPage,
                    onLaterPages=_onPage)

    def _page_header(self, canvas, doc):
        canvas.saveState()
        # self._print_desc_company(canvas, doc, 255*mm)
        self._print_actual_date(canvas, doc, 262*mm)
        self._print_title_doc(canvas, doc, 238*mm)
        # self._print_footer(canvas, doc, 10.5*mm)

        canvas.restoreState()

    def _print_num_folio(self, canvas, doc, y):
        start_folio = self.kwargs.get('start_folio')
        page = doc.page
        if start_folio is not None:
            page = page+start_folio-1
        numPag = str(f'N° Folio :  {page}')
        canvas.setFont('tahoma', 10)
        canvas.drawString(175*mm, y, numPag)
        return canvas, doc

    # FECHA Y NUMERO DE PAGINA EN ENCABEZADO
    def _print_actual_date(self, canvas, doc, y):
        pag_number = doc.page
        cd = timezone.now()
        data_table = []
        style_table = [('FONT', (0, 0), (-1, -1), 'tahoma', 8),]
        table_col_width = [9*mm, 3*mm, 25*mm]
        table_row_height = 2*[3*mm]
        data_table.append([
                'Fecha', ':', '%s/%s/%s'%(cd.day, cd.month, cd.year)
            ])
        data_table.append([
                'Página', ':', pag_number
            ])
        width = 40*mm
        height = 10*mm
        x = 178*mm

        f = Table(data_table,
                colWidths=table_col_width,
                rowHeights=table_row_height)
        f.wrapOn(canvas, width, height)
        f.setStyle(TableStyle(style_table))
        f.drawOn(canvas, x, y)
        return canvas, doc

    # ENCABEZADO CON DATOS DE LA EMPRESA
    def _print_desc_company(self, canvas, doc, y):
        company = self.kwargs.get('current_company', None)
        data_table = []
        style_table = [('FONT', (0, 0), (-1, -1), 'tahoma', 8),]
        table_col_width = [20*mm, 3*mm, 25*mm]
        table_row_height = 5*[3*mm]
        if company is not None:
            data_table.append([
                'RAZÓN SOCIAL', ':', _if_none(company.name, "").upper()[0:25]
            ])
            data_table.append([
                'R.U.T', ':', _if_none(company.rut, "").upper()[0:25]
            ])
            data_table.append([
                'DIRECCIÓN', ':', _if_none(company.address, "").upper()[0:25]
            ])
            data_table.append([
                'COMUNA', ':', _if_none(company.commune.name, "").upper()[0:25]
            ])
            data_table.append([
                'GIRO', ':', _if_none(company.commercial_business, "").upper()[0:25]
            ])

            width = 50*mm
            height = 15*mm
            x = 8*mm
            f = Table(data_table,
                    colWidths=table_col_width,
                    rowHeights=table_row_height)

            f.wrapOn(canvas, width, height)
            f.setStyle(TableStyle(style_table))

            canvas.setFont('tahoma', 8)
            f.drawOn(canvas, x, y)
        return canvas, doc

    # TITULO DE DOCUMENTO
    def _print_title_doc(self, canvas, doc, y):
        reserva = self.kwargs.get('reserva', None)
        canvas.setFillColor(HexColor('#00945e'))
        canvas.setFont('tahoma_bold', 16)
        text = f'Reserva: {reserva.departamento.nombre_dpto}'
        #text_width = stringWidth(text, 'tahoma_bold', 13)
        canvas.drawString(10*mm, y, text)
        canvas.setFillColor(HexColor('#000000'))
        return canvas, doc

    # CUERPO DE LA TABLA
    def _body(self):
        data_table = []
        style_table = []
        reserva = self.kwargs.get('reserva', None)

        # PARTE 1
        data_table.append([
            Paragraph(str(f'Datos '), TITLE_CENTER_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str("Correo:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(_if_none(reserva.usuario.correo, "")), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str("Cliente:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(_if_none(reserva.usuario.nombre, "")), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str("Días:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(_if_none(f'{reserva.dias} Días', "")), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str("Departamento:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(_if_none(reserva.departamento.nombre_dpto, "")), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str("Region:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(_if_none(reserva.departamento.id_comuna.id_region, "")), TEXT_LEFT_TABLE),
            Paragraph(str("Comuna:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(_if_none(reserva.departamento.id_comuna, "")), TEXT_LEFT_TABLE),
        ])
        data_table.append([
            Paragraph(str("Tour:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(_if_none(reserva.tour, "")), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])

        style_table.append(('VALIGN', (0, 0), (-1, -1), 'MIDDLE'))

        col_size = [40*mm, 70*mm, 47*mm, 39*mm]

        table = Table(
            data=data_table,
            colWidths=col_size,
            spaceBefore=15,
            spaceAfter=15,
        )
        table.setStyle(TableStyle(style_table))
        self.Story.append(table)

    def _body1(self):
        data_table = []
        style_table = []
        reserva = self.kwargs.get('reserva', None)
        valor_reserva = (reserva.dias * reserva.departamento.tarifa_diaria)
        valor_anticipo = (valor_reserva*0.30)
        valor_total_tour = (valor_anticipo+reserva.tour.valor)
        valo_total = (valor_reserva+reserva.tour.valor)

        data_table.append([
            Paragraph(str(f'Costos '), TITLE_CENTER_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str(f'Valor {reserva.tour.tipo_tour.descripcion}:'), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(f'${intcomma(reserva.tour.valor)}'), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str("Valor Reserva Díario:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(f'${intcomma(reserva.departamento.tarifa_diaria)}'), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str("Valor Anticipo Reserva:"), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(f'${intcomma(valor_anticipo)}'), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str(f'Valor Reserva + {reserva.tour.tipo_tour.descripcion}:'), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(f'${intcomma(valor_total_tour)}'), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str(f'Valor Reserva Total - {reserva.tour.tipo_tour.descripcion}:'), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(f'${intcomma(valor_reserva)}'), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])
        data_table.append([
            Paragraph(str(f'Valor Reserva Total + {reserva.tour.tipo_tour.descripcion}:'), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(f'${intcomma(valo_total)}'), TEXT_LEFT_TABLE),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
            Paragraph(str(""), TITLE_LEFT_TABLE_BOLD),
        ])

        style_table.append(('VALIGN', (0, 0), (-1, -1), 'MIDDLE'))

        col_size = [40*mm, 70*mm, 47*mm, 39*mm]

        table = Table(
            data=data_table,
            colWidths=col_size,
            spaceBefore=20,
            spaceAfter=40,
        )
        table.setStyle(TableStyle(style_table))
        self.Story.append(table)

    def _body2(self):
        data_table = []
        style_table = []

        # PARTE 1
        data_table.append([
            Paragraph(str("Pagar el resto del valor una ves se realize la entrega de las llaves."), TEXT_LEFT_TABLE),
        ])
        data_table.append([
            Paragraph(str("Todo daño, ruido u molestias recibidas durante la estancia del cliente, se vera reflejado en un cobro adicional en su boleta."), TEXT_LEFT_TABLE),
        ])

        style_table.append(('VALIGN', (0, 0), (-1, -1), 'MIDDLE'))

        col_size = [196*mm]

        table = Table(
            data=data_table,
            colWidths=col_size,
            spaceBefore=20,
            spaceAfter=0,
        )
        table.setStyle(TableStyle(style_table))
        self.Story.append(table)

    #  # FOOTER
    # def _print_footer(self, canvas, doc, y):

    #     img_url = os.path.join(
    #         BASE_DIR, 'app', 'static',  'assets', 'img', 'brand',
    #             'logo_remu_report_footer.jpg')

    #     width = 48*mm
    #     height = 9*mm
    #     x = 10*mm
    #     canvas.drawImage(img_url, x, y, width, height)

    #     canvas.setFont('tahoma_bold', 9)
    #     canvas.drawString(181 * mm, y+3*mm,
    #                     'transtecnia.cl' )

    #     return canvas, doc

def _if_none(value, message_show):
    if value is None:
        return message_show
    else:
        return value

def clean_intcomma(value_with_intcomma):
    if value_with_intcomma == 0:
        return 0
    return int(value_with_intcomma.replace(".", ""))


from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import relationship

from ._metadata_meta import _META_SOMETHING

def CONTEXT_METADATA(metadated_classname, SQLAlchemyContextType):

    tname_prefix = f'META_CTX<{metadated_classname}>'

    class ToInherit:

        @declared_attr
        def contextid(cls):
            return Column(Integer, ForeignKey(SQLAlchemyContextType.id), nullable=False)

        @declared_attr
        def context(cls):
            return relationship(SQLAlchemyContextType, foreign_keys=[cls.contextid])

    columns = {
        "id": Column(Integer, primary_key=True),
        "contextid": None, # already in ToInherit for sqlalchemy reason
        "context": None    # already in ToInherit for sqlalchemy reason
    }

    return _META_SOMETHING(tname_prefix, columns, ToInherit)

if __name__ == "__main__":
    from persistent_to_disk import create_session
    from persistent.base_type import _String, BasicEntity, STRING_SIZE

    from sqlalchemy import String

    columns = {
        "id": Column(Integer, primary_key=True),
        "value": Column(String(STRING_SIZE), unique=True),
        "SUPERCOL": Column(Integer, default=666),
    }

    Test = BasicEntity("thebaseobject", columns)

    v1 = Test(value="bonjour")

    session = create_session()

    v2 = Test.GET_CREATE(session, value="bonjour2")
    v3 = Test.GET_CREATE(session, value="bonjour3")
    v4 = Test.GET_CREATE(session, value="bonjour4")

    session.add(v1)
    try:
        session.commit()
    except:
        print("Rollbacking")
        session.rollback()
        v1 = Test.GET_CREATE(session, value="bonjour")

    CTX_STRING = CONTEXT_METADATA(Test.__tablename__, Test)

    print(CTX_STRING.GET(session, context=v1))
    print(CTX_STRING.GET_CREATE(session, context=v2))
    print(CTX_STRING.NEW(session, context=v1))
    print(CTX_STRING.NEW(session, context=v1))
    print(CTX_STRING.GET(session, context=v1))

    print(CTX_STRING.GET_COND(session, CTX_STRING.context == v2))

    print(CTX_STRING.GET_CREATE(session, context=v1))
    print(CTX_STRING.GET_CREATE(session, context=v3))

    CTX_STRING.DELETE(session, context=v1, id=6)
    print(CTX_STRING.NEW(session, context=v4))

    CTX_STRING.DELETE(session, context=v1)

    print(CTX_STRING.GET(session, context=v1))
    print(CTX_STRING.GET_CREATE(session, context=v1))


"""
7.7 Рекурсивные функции

Великий подвиг 8. Вводится список из целых чисел в одну строчку через пробел.
Необходимо выполнить его сортировку по возрастанию с помощью алгоритма сортировки слиянием.
Функция должна возвращать новый отсортированный список.
Вызовите результирующую функцию сортировки для введенного списка и отобразите результат
на экран в виде последовательности чисел, записанных через пробел.

Подсказка. Для разбиения списка и его последующей сборки используйте рекурсивные функции.

P. S. Теория сортировки в видео предыдущего шага.

Sample Input: 8 11 -6 3 0 1 1
Sample Output: -6 0 1 1 3 8 11
"""

list_in = [int(num) for num in input().split()]

def rec_sort(f_len, s_len):
    """
    Функция слияния с сохранением сортировки
    :param f_len:
    :param s_len:
    :return:
    """
    result_list = [] # сюда будем сохранять знанчения из двух списков по возрастанию
    len_f_len = len(f_len) # Длинна первого списка
    len_s_len = len(s_len) # Длинна второго списка

    i = 0 # Индекс первого списка
    j = 0 # Индекс второго списка

    while i < len_f_len and j < len_s_len: # Пока длинна одного или другова списка меньше счетчкиков
        if f_len[i] <= s_len[j]: # Если знанчение списка f_len[i] меньше или равно s_len[j]
            result_list.append(f_len[i]) # Добавляем его в список result_list
            i += 1 # Увеличиваем счетчки на 1
        elif f_len[i] >= s_len[j]: # Если знанчение списка s_len[j] меньше или равно f_len[i]
            result_list.append(s_len[j]) # Добавляем его в список result_list
            j += 1 # Увеличиваем счетчки на 1
    result_list += f_len[i:] + s_len[j:] # Добавляем оставшиеся элементы в фианальный список result_list
    return result_list

def rec_div(list_in):

    num = len(list_in) // 2 # Получаем число которае будет серединой массива
    f_len = list_in[:num] # Первая часть массива
    s_len = list_in[num:] # Вторая часть массива
    if len(f_len) > 1: # Если длина f_len больше единицы, то продолжаем делить
        f_len = rec_div(f_len) # Сюда передается список f_len
    if len(s_len) > 1: # Если длина s_len больше единицы, то продолжаем делить
        s_len = rec_div(s_len) # Сюда передается список s_len

    return rec_sort(f_len, s_len) # Слияние двух отсортированных массивов

print(*rec_div(list_in))


#  Author:   Niels Nuyttens  <niels@nannyml.com>
#
#  License: Apache Software License 2.0

"""Contains the results of the data reconstruction drift calculation and provides plotting functionality."""
from __future__ import annotations

from collections import namedtuple
from typing import List, Optional

import pandas as pd
import plotly.graph_objects as go

from nannyml._typing import Key
from nannyml.base import PerMetricResult
from nannyml.exceptions import InvalidArgumentsException
from nannyml.plots.blueprints.comparisons import ResultCompareMixin
from nannyml.plots.blueprints.metrics import plot_metric
from nannyml.usage_logging import UsageEvent, log_usage

Metric = namedtuple("Metric", "display_name column_name")

class Result(PerMetricResult[Metric], ResultCompareMixin):
    """Class wrapping the results of the data reconstruction drift calculator and providing plotting functionality."""

    def __init__(
        self,
        results_data: pd.DataFrame,
        column_names: List[str],
        categorical_column_names: List[str],
        continuous_column_names: List[str],
        timestamp_column_name: Optional[str] = None,
    ):
        """
        Parameters
        ----------
        results_data: pd.DataFrame
            Results data returned by a DataReconstructionDriftCalculator.
        column_names: List[str]
            A list of column names indicating which columns contain feature values.
        categorical_column_names : List[str]
            Subset of categorical features to be included in calculation.
        continuous_column_names : List[str]
            Subset of continuous features to be included in calculation.
        timestamp_column_name: Optional[str], default=None
            The name of the column containing the timestamp of the model prediction.
            If not given, plots will not use a time-based x-axis but will use the index of the chunks instead.
        """
        metric = Metric(display_name='Reconstruction error', column_name='reconstruction_error')
        super().__init__(results_data, [metric])

        self.column_names = column_names
        self.categorical_column_names = categorical_column_names
        self.continuous_column_names = continuous_column_names
        self.timestamp_column_name = timestamp_column_name

    def keys(self) -> List[Key]:
        """
        Creates a list of keys where each Key is a `namedtuple('Key', 'properties display_names')`
        """
        return [Key(properties=('reconstruction_error',), display_names=('Reconstruction error',))]

    @log_usage(UsageEvent.MULTIVAR_DRIFT_PLOT, metadata_from_kwargs=['kind'])
    def plot(self, kind: str = 'drift', *args, **kwargs) -> go.Figure:
        """Renders plots for metrics returned by the multivariate data reconstruction calculator.

        Parameters
        ----------
        kind: str, default='drift'
            The kind of plot you want to have. Value can currently only be 'drift'.

        Raises
        ------
        InvalidArgumentsException: when an unknown plot ``kind`` is provided.

        Returns
        -------
        fig: :class:`plotly.graph_objs._figure.Figure`
            A :class:`~plotly.graph_objs._figure.Figure` object containing the requested drift plot.

            Can be saved to disk using the :meth:`~plotly.graph_objs._figure.Figure.write_image` method
            or shown rendered on screen using the :meth:`~plotly.graph_objs._figure.Figure.show` method.

        Examples
        --------
        >>> import nannyml as nml
        >>> # Load synthetic data
        >>> reference, analysis, _ = nml.load_synthetic_car_loan_dataset()
        >>> non_feature_columns = ['timestamp', 'y_pred_proba', 'y_pred', 'repaid']
        >>> feature_column_names = [
        ...     col for col in reference.columns
        ...     if col not in non_feature_columns
        >>> ]
        >>> calc = nml.DataReconstructionDriftCalculator(
        ...     column_names=feature_column_names,
        ...     timestamp_column_name='timestamp',
        ...     chunk_size=5000
        >>> )
        >>> calc.fit(reference)
        >>> results = calc.calculate(analysis)
        >>> figure = results.plot()
        >>> figure.show()
        """
        if kind == 'drift':
            return plot_metric(
                self,
                title='Multivariate Drift (PCA Reconstruction Error)',
                metric_display_name='Reconstruction Error',
                metric_column_name='reconstruction_error',
            )
        else:
            raise InvalidArgumentsException(f"unknown plot kind '{kind}'. " f"Please provide one of: ['drift'].")


from partie1 import *

X = np.matrix([[3], [4], [0]])
Y = np.matrix([[0], [0], [5]])
H = np.matrix([[0.64, -0.48, 0.6], [-0.48, 0.36, 0.8], [0.6, 0.8, 0]])

def test_creerHH():
    test = 0
    RES = creerHH(X, Y)
    (n, m) = np.shape(RES)
    for i in range (n):
        for j in range (m):
            if float(int(RES[i, j] * 10**14)/10**14) == H[i, j] :
                test += 1
    if test == n * m :
        print("Test de creerHH... ok")
    else :

        print("Test de creerHH... failed")

test_creerHH()

def test_mat_vect():
    test = 0
    RES = mat_vect(creerHH(X, Y), X)
    (n, m) = np.shape(RES)
    for k in range (m):
        if RES[k] == Y[k] :
            test += 1
    if test == m :
        print("Test de mat_vect... ok")
    else :

        print("Test de mat_vect... failed")

test_mat_vect()


from annoying.decorators import render_to
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.http import HttpResponseBadRequest
from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from models import Owner
from forms import EditOwner
from request.models import Request
from django.conf import settings

@render_to('hello/home_base.html')
def home(request):
    owners = Owner.objects.filter()
    return {'owners': owners}

@render_to('hello/requests.html')
def latest_requests(request):
    requests = Request.objects.all()[:10]
    return {'requests': requests}

@login_required
def edit_home(request, ajax=False):
    context = {}
    context.update(csrf(request))
    owners = Owner.objects.filter()
    if owners.exists():
        owner = owners[0]
        form = EditOwner(instance=owner)
    else:
        form = EditOwner()
    if request.POST:
        if owners.exists():
            # Editing of owner existing data
            form = EditOwner(request.POST, request.FILES, instance=owners[0])
        else:
            # No data exists
            form = EditOwner(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            if ajax:
                return HttpResponse('ok')
        else:
            if ajax:
                return HttpResponseBadRequest('Wrong')

    context.update({
        'form': form,
        'MEDIA_URL': settings.MEDIA_URL
    })
    return render_to_response('hello/home_base.html', context)


"""Provide class for root sum of squares of edgewise timeseries."""

# Authors: Leonard Sasse <l.sasse@fz-juelich.de>
#          Nicolás Nieto <n.nieto@fz-juelich.de>
#          Sami Hamdan <s.hamdan@fz-juelich.de>
#          Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL

from typing import Any, ClassVar, Dict, List, Optional, Set, Union

import numpy as np

from ..api.decorators import register_marker
from ..utils import logger
from .base import BaseMarker
from .parcel_aggregation import ParcelAggregation
from .utils import _ets

@register_marker
class RSSETSMarker(BaseMarker):
    """Class for root sum of squares of edgewise timeseries.

    Parameters
    ----------
    parcellation : str or list of str
        The name(s) of the parcellation(s). Check valid options by calling
        :func:`.list_parcellations`.
    agg_method : str, optional
        The method to perform aggregation using. Check valid options in
        :func:`.get_aggfunc_by_name` (default "mean").
    agg_method_params : dict, optional
        Parameters to pass to the aggregation function. Check valid options in
        :func:`.get_aggfunc_by_name` (default None).
    masks : str, dict or list of dict or str, optional
        The specification of the masks to apply to regions before extracting
        signals. Check :ref:`Using Masks <using_masks>` for more details.
        If None, will not apply any mask (default None).
    name : str, optional
        The name of the marker. If None, will use the class name (default
        None).

    """

    _DEPENDENCIES: ClassVar[Set[str]] = {"nilearn"}

    def __init__(
        self,
        parcellation: Union[str, List[str]],
        agg_method: str = "mean",
        agg_method_params: Optional[Dict] = None,
        masks: Union[str, Dict, List[Union[Dict, str]], None] = None,
        name: Optional[str] = None,
    ) -> None:
        self.parcellation = parcellation
        self.agg_method = agg_method
        self.agg_method_params = agg_method_params
        self.masks = masks
        super().__init__(name=name)

    def get_valid_inputs(self) -> List[str]:
        """Get valid data types for input.

        Returns
        -------
        list of str
            The list of data types that can be used as input for this marker.

        """
        return ["BOLD"]

    def get_output_type(self, input_type: str) -> str:
        """Get output type.

        Parameters
        ----------
        input_type : str
            The data type input to the marker.

        Returns
        -------
        str
            The storage type output by the marker.

        """
        return "timeseries"

    def compute(
        self,
        input: Dict[str, Any],
        extra_input: Optional[Dict] = None,
    ) -> Dict:
        """Compute.

        Take a timeseries of brain areas, and calculate timeseries for each
        edge according to the method outlined in [1]_. For more information,
        check https://github.com/brain-networks/edge-ts/blob/master/main.m

        Parameters
        ----------
        input : dict
            The BOLD data as dictionary.
        extra_input : dict, optional
            The other fields in the pipeline data object (default None).

        Returns
        -------
        dict
            The computed result as dictionary. The dictionary has the following
            keys:

            * ``data`` : the actual computed values as a numpy.ndarray
            * ``col_names`` : the column labels for the computed values as list

        References
        ----------
        .. [1] Zamani Esfahlani et al. (2020)
               High-amplitude cofluctuations in cortical activity drive
               functional connectivity
               doi: 10.1073/pnas.2005531117

        """
        logger.debug("Calculating root sum of squares of edgewise timeseries.")
        # Initialize a ParcelAggregation
        parcel_aggregation = ParcelAggregation(
            parcellation=self.parcellation,
            method=self.agg_method,
            method_params=self.agg_method_params,
            masks=self.masks,
        )
        # Compute the parcel aggregation
        out = parcel_aggregation.compute(input=input, extra_input=extra_input)
        edge_ts, _ = _ets(out["data"])
        # Compute the RSS
        out["data"] = np.sum(edge_ts**2, 1) ** 0.5
        # Make it 2D
        out["data"] = out["data"][:, np.newaxis]
        # Set correct column label
        out["col_names"] = ["root_sum_of_squares_ets"]
        return out