import numpy as np
import cv2
from hog_fitures import HOG_fitures
from utils import *

fhog = HOG_fitures()

class KCFTracker:
    def __init__(self, hog=False, fixed_window=True, multiscale=False):
        self.lambda_reg          = 0.0001  # регуляризация
        self.padding             = 2.5     # дополнительная область вокруг цели
        self.output_sigma_factor = 0.125   # полоса пропускания гауссовой цели

        if(hog):  # HOG
            # VOT
            self.interp_factor = 0.012     # коэффициент линейной интерполяции для адаптации
            self.sigma = 0.6               # пропускная способность гауссовского ядра
            self.cell_size = 4             # HOG размер ячейки
            self._hogfeatures = True
        else:
            self.interp_factor = 0.075
            self.sigma = 0.2
            self.cell_size = 1
            self._hogfeatures = False

        if(multiscale):
            self.template_size = 96        # размер шаблона
            self.scale_step = 1.05         # шаг шкалы для многомасштабной оценки
            self.scale_weight = 0.96       # для снижения показателей обнаружения других шкал для дополнительной стабильности
        elif(fixed_window):
            self.template_size = 96
            self.scale_step = 1
        else:
            self.template_size = 1
            self.scale_step = 1

        self._tmpl_sz = [0, 0]
        self._roi = [0., 0., 0., 0.]
        self.size_patch = [0, 0, 0]
        self._scale = 1.
        self._alphaf = None
        self._prob = None
        self._tmpl = None
        self.hann = None

    def subPixelPeak(self, left, center, right):
        divisor = 2 * center - right - left
        return (0 if abs(divisor) < 1e-3 else 0.5 * (right - left) / divisor)

    def createHanningMats(self):
        hann2t, hann1t = np.ogrid[0:self.size_patch[0], 0:self.size_patch[1]]

        hann1t = 0.5 * (1 - np.cos(2 * np.pi * hann1t / (self.size_patch[1] - 1)))
        hann2t = 0.5 * (1 - np.cos(2 * np.pi * hann2t / (self.size_patch[0] - 1)))
        hann2d = hann2t * hann1t

        if(self._hogfeatures):
            hann1d = hann2d.reshape(self.size_patch[0] * self.size_patch[1])
            self.hann = np.zeros((self.size_patch[2], 1), np.float32) + hann1d
        else:
            self.hann = hann2d
        self.hann = self.hann.astype(np.float32)

    def createGaussianPeak(self, sizey, sizex):
        syh, sxh = sizey / 2, sizex / 2
        output_sigma = np.sqrt(sizex * sizey) / self.padding * self.output_sigma_factor
        mult = -0.5 / (output_sigma * output_sigma)
        y, x = np.ogrid[0:sizey, 0:sizex]
        y, x = (y - syh)**2, (x - sxh)**2
        res = np.exp(mult * (y + x))
        return fftd(res)

    def gaussianCorrelation(self, x1, x2):
        if(self._hogfeatures):
            c = np.zeros((self.size_patch[0], self.size_patch[1]), np.float32)
            for i in range(self.size_patch[2]):
                x1aux = x1[i, :].reshape((self.size_patch[0], self.size_patch[1]))
                x2aux = x2[i, :].reshape((self.size_patch[0], self.size_patch[1]))
                caux = cv2.mulSpectrums(fftd(x1aux), fftd(x2aux), 0, conjB=True)
                caux = real(fftd(caux, True))
                #caux = rearrange(caux)
                c += caux
            c = rearrange(c)
        else:
            c = cv2.mulSpectrums(fftd(x1), fftd(x2), 0, conjB=True)   # 'conjB=' is necessary!
            c = fftd(c, True)
            c = real(c)
            c = rearrange(c)

        if(x1.ndim == 3 and x2.ndim == 3):
            d = (np.sum(x1[:, :, 0] * x1[:, :, 0]) + np.sum(x2[:, :, 0] * x2[:, :, 0]) - 2.0 * c) / (self.size_patch[0] * self.size_patch[1] * self.size_patch[2])
        elif(x1.ndim == 2 and x2.ndim == 2):
            d = (np.sum(x1 * x1) + np.sum(x2 * x2) - 2.0 * c) / (self.size_patch[0] * self.size_patch[1] * self.size_patch[2])

        d = d * (d >= 0)
        d = np.exp(-d / (self.sigma * self.sigma))

        return d

    def getFeatures(self, image, inithann, scale_adjust=1.0):
        extracted_roi = [0, 0, 0, 0]
        cx = self._roi[0] + self._roi[2] / 2
        cy = self._roi[1] + self._roi[3] / 2

        if(inithann):
            padded_w = self._roi[2] * self.padding
            padded_h = self._roi[3] * self.padding

            if(self.template_size > 1):
                if(padded_w >= padded_h):
                    self._scale = padded_w / float(self.template_size)
                else:
                    self._scale = padded_h / float(self.template_size)
                self._tmpl_sz[0] = int(padded_w / self._scale)
                self._tmpl_sz[1] = int(padded_h / self._scale)
            else:
                self._tmpl_sz[0] = int(padded_w)
                self._tmpl_sz[1] = int(padded_h)
                self._scale = 1.

            if(self._hogfeatures):
                self._tmpl_sz[0] = int(self._tmpl_sz[0]) // (2 * self.cell_size) * 2 * self.cell_size + 2 * self.cell_size
                self._tmpl_sz[1] = int(self._tmpl_sz[1]) // (2 * self.cell_size) * 2 * self.cell_size + 2 * self.cell_size
            else:
                self._tmpl_sz[0] = int(self._tmpl_sz[0]) // 2 * 2
                self._tmpl_sz[1] = int(self._tmpl_sz[1]) // 2 * 2

        extracted_roi[2] = int(scale_adjust * self._scale * self._tmpl_sz[0])
        extracted_roi[3] = int(scale_adjust * self._scale * self._tmpl_sz[1])
        extracted_roi[0] = int(cx - extracted_roi[2] / 2)
        extracted_roi[1] = int(cy - extracted_roi[3] / 2)

        z = subwindow(image, extracted_roi, cv2.BORDER_REPLICATE)
        if(z.shape[1] != self._tmpl_sz[0] or z.shape[0] != self._tmpl_sz[1]):
            z = cv2.resize(z, tuple(self._tmpl_sz))

        if(self._hogfeatures):
            mapp = {'sizeX': 0, 'sizeY': 0, 'numFeatures': 0, 'map': 0}
            mapp = fhog.getFeatureMaps(z, self.cell_size, mapp)
            mapp = fhog.normalizeAndTruncate(mapp, 0.2)
            mapp = fhog.PCAFeatureMaps(mapp)
            self.size_patch = list(map(int, [mapp['sizeY'], mapp['sizeX'], mapp['numFeatures']]))
            FeaturesMap = mapp['map'].reshape((self.size_patch[0] * self.size_patch[1], self.size_patch[2])).T
        else:
            if(z.ndim == 3 and z.shape[2] == 3):
                FeaturesMap = cv2.cvtColor(z, cv2.COLOR_BGR2GRAY)
            elif(z.ndim == 2):
                FeaturesMap = z
            FeaturesMap = FeaturesMap.astype(np.float32) / 255.0 - 0.5
            self.size_patch = [z.shape[0], z.shape[1], 1]

        if(inithann):
            self.createHanningMats()

        FeaturesMap = self.hann * FeaturesMap
        return FeaturesMap

    def detect(self, z, x):
        k = self.gaussianCorrelation(x, z)
        res = real(fftd(complex_multiplication(self._alphaf, fftd(k)), True))

        _, pv, _, pi = cv2.minMaxLoc(res)
        p = [float(pi[0]), float(pi[1])]

        if(pi[0] > 0 and pi[0] < res.shape[1] - 1):
            p[0] += self.subPixelPeak(res[pi[1], pi[0] - 1], pv, res[pi[1], pi[0] + 1])
        if(pi[1] > 0 and pi[1] < res.shape[0] - 1):
            p[1] += self.subPixelPeak(res[pi[1] - 1, pi[0]], pv, res[pi[1] + 1, pi[0]])

        p[0] -= res.shape[1] / 2.
        p[1] -= res.shape[0] / 2.

        return p, pv

    def train(self, x, train_interp_factor):
        k = self.gaussianCorrelation(x, x)
        alphaf = complex_division(self._prob, fftd(k) + self.lambda_reg)

        self._tmpl = (1 - train_interp_factor) * self._tmpl + train_interp_factor * x
        self._alphaf = (1 - train_interp_factor) * self._alphaf + train_interp_factor * alphaf

    def init(self, roi, image):
        self._roi = list(map(float, roi))
        assert(roi[2] > 0 and roi[3] > 0)
        self._tmpl = self.getFeatures(image, 1)
        self._prob = self.createGaussianPeak(self.size_patch[0], self.size_patch[1])
        self._alphaf = np.zeros((self.size_patch[0], self.size_patch[1], 2), np.float32)
        self.train(self._tmpl, 1.0)

    def update(self, image):
        if(self._roi[0] + self._roi[2] <= 0):
            self._roi[0] = -self._roi[2] + 1
        if(self._roi[1] + self._roi[3] <= 0):
            self._roi[1] = -self._roi[2] + 1
        if(self._roi[0] >= image.shape[1] - 1):
            self._roi[0] = image.shape[1] - 2
        if(self._roi[1] >= image.shape[0] - 1):
            self._roi[1] = image.shape[0] - 2

        cx = self._roi[0] + self._roi[2] / 2.
        cy = self._roi[1] + self._roi[3] / 2.

        loc, peak_value = self.detect(self._tmpl, self.getFeatures(image, 0, 1.0))

        if(self.scale_step != 1):
            # Маленький scale
            new_loc1, new_peak_value1 = self.detect(self._tmpl, self.getFeatures(image, 0, 1.0 / self.scale_step))
            # Большой scale
            new_loc2, new_peak_value2 = self.detect(self._tmpl, self.getFeatures(image, 0, self.scale_step))

            if(self.scale_weight * new_peak_value1 > peak_value and new_peak_value1 > new_peak_value2):
                loc = new_loc1
                peak_value = new_peak_value1
                self._scale /= self.scale_step
                self._roi[2] /= self.scale_step
                self._roi[3] /= self.scale_step
            elif(self.scale_weight * new_peak_value2 > peak_value):
                loc = new_loc2
                peak_value = new_peak_value2
                self._scale *= self.scale_step
                self._roi[2] *= self.scale_step
                self._roi[3] *= self.scale_step

        self._roi[0] = cx - self._roi[2] / 2.0 + loc[0] * self.cell_size * self._scale
        self._roi[1] = cy - self._roi[3] / 2.0 + loc[1] * self.cell_size * self._scale

        if(self._roi[0] >= image.shape[1] - 1):
            self._roi[0] = image.shape[1] - 1
        if(self._roi[1] >= image.shape[0] - 1):
            self._roi[1] = image.shape[0] - 1
        if(self._roi[0] + self._roi[2] <= 0):
            self._roi[0] = -self._roi[2] + 2
        if(self._roi[1] + self._roi[3] <= 0):
            self._roi[1] = -self._roi[3] + 2
        assert(self._roi[2] > 0 and self._roi[3] > 0)

        x = self.getFeatures(image, 0, 1.0)
        self.train(x, self.interp_factor)

        return self._roi


#! /usr/bin/env python

"""
Module with fake companion injection functions.
"""

__author__ = 'Carlos Alberto Gomez Gonzalez, Valentin Christiaens'
__all__ = ['collapse_psf_cube',
           'normalize_psf',
           'cube_inject_companions',
           'generate_cube_copies_with_injections',
           'frame_inject_companion']

from multiprocessing import cpu_count
import numpy as np
from scipy import stats
from scipy.interpolate import interp1d
from packaging import version
import photutils
try:
    from photutils.aperture import aperture_photometry, CircularAperture
except:
    from photutils import aperture_photometry, CircularAperture
if version.parse(photutils.__version__) >= version.parse('0.3'):
    # for photutils version >= '0.3' use photutils.centroids.centroid_com
    from photutils.centroids import centroid_com as cen_com
else:
    # for photutils version < '0.3' use photutils.centroid_com
    import photutils.centroid_com as cen_com
from ..preproc import (cube_crop_frames, frame_shift, frame_crop, cube_shift,
                       frame_rotate)
from ..var import (frame_center, fit_2dgaussian, fit_2dairydisk, fit_2dmoffat,
                   get_circle, get_annulus_segments, dist_matrix)
from ..config.utils_conf import print_precision, check_array, pool_map, iterable

def cube_inject_companions(array, psf_template, angle_list, flevel, rad_dists,
                           plsc=None, n_branches=1, theta=0, imlib='vip-fft',
                           interpolation='lanczos4', transmission=None,
                           radial_gradient=False, full_output=False,
                           verbose=False, nproc=1):
    """ Injects fake companions in branches, at given radial distances.

    Parameters
    ----------
    array : 3d/4d numpy ndarray
        Input cube. This is copied before the injections take place, so
        ``array`` is never modified.
    psf_template : 2d/3d numpy ndarray
        [for a 3D input array] 2d array with the normalized PSF template, with
        an odd or even shape. The PSF image must be centered wrt to the array.
        Therefore, it is recommended to run the function ``normalize_psf`` to
        generate a centered and flux-normalized PSF template.
        It can also be a 3D array, but length should match that of ADI cube.
        [for a 4D input array] In the ADI+mSDI case, it must be a 3d array
        (matching spectral dimensions).
    angle_list : 1d numpy ndarray
        List of parallactic angles, in degrees.
    flevel : float or 1d array or 2d array
        Factor for controlling the brightness of the fake companions. If a float,
        the same flux is used for all injections.
        [3D input cube]: if a list/1d array is provided, it should have same
        length as number of frames in the 3D cube (can be used to take into
        account varying observing conditions or airmass).
        [4D (ADI+mSDI) input cube]: if a list/1d array should have the same
        length as the number of spectral channels (i.e. provide a spectrum). If
        a 2d array, it should be n_wavelength x n_frames (can e.g. be used to
        inject a spectrum in varying conditions).
    plsc : float or None
        Value of the plsc in arcsec/px. Only used for printing debug output when
        ``verbose=True``.
    rad_dists : float, list or array 1d
        Vector of radial distances of fake companions in pixels.
    n_branches : int, optional
        Number of azimutal branches.
    theta : float, optional
        Angle in degrees for rotating the position of the first branch that by
        default is located at zero degrees. Theta counts counterclockwise from
        the positive x axis.
    imlib : str, optional
        See the documentation of the ``vip_hci.preproc.frame_shift`` function.
    interpolation : str, optional
        See the documentation of the ``vip_hci.preproc.frame_shift`` function.
    transmission: numpy array, optional
        Radial transmission of the coronagraph, if any. Array with either
        2 x n_rad, 1+n_ch x n_rad columns. The first column should contain the
        radial separation in pixels, while the other column(s) are the
        corresponding off-axis transmission (between 0 and 1), for either all,
        or each spectral channel (only relevant for a 4D input cube).
    radial_gradient: bool, optional
        Whether to apply a radial gradient to the psf image at the moment of
        injection. By default False, i.e. the flux of the psf image is scaled
        only considering the value of tramnsmission at the exact radius the
        companion is injected. Setting it to False may better represent the
        transmission at the very edge of a physical mask though.
    full_output : bool, optional
        Returns the ``x`` and ``y`` coordinates of the injections, additionally
        to the new array.
    verbose : bool, optional
        If True prints out additional information.
    nproc: int or None, optional
        Number of CPUs to use for multiprocessing. If None, will be
        automatically set to half the number of available CPUs.

    Returns
    -------
    array_out : numpy ndarray
        Output array with the injected fake companions.
    positions : list of tuple(y, x)
        [full_output] Coordinates of the injections in the first frame (and
        first wavelength for 4D cubes).
    psf_trans: numpy ndarray
        [full_output & transmission != None] Array with injected psf affected
        by transmission (serves to check radial transmission)

    """
    if nproc is None:
        nproc = cpu_count()//2

    def _cube_inject_adi(array, psf_template, angle_list, flevel, plsc,
                         rad_dists, n_branches=1, theta=0, imlib='vip-fft',
                         interpolation='lanczos4', transmission=None,
                         radial_gradient=False, verbose=False):
        if np.isscalar(flevel):
            flevel = np.ones_like(angle_list)*flevel

        if transmission is not None:
            # last radial separation should be beyond the edge of frame
            interp_trans = interp1d(transmission[0], transmission[1])

        ceny, cenx = frame_center(array[0])
        nframes = array.shape[-3]
        size_fc = psf_template.shape[-1]

        positions = []
        # fake companion cube
        fc_fr = np.zeros([nframes, size_fc, size_fc])
        if psf_template.ndim == 2:
            for fr in range(nframes):
                fc_fr[fr] = psf_template
        else:
            for fr in range(nframes):
                fc_fr[fr] = psf_template[fr]

        psf_trans = None
        array_out = array.copy()

        for branch in range(n_branches):
            ang = (branch * 2 * np.pi / n_branches) + np.deg2rad(theta)

            if verbose:
                print('Branch {}:'.format(branch+1))

            for rad in rad_dists:
                fc_fr_rad = fc_fr.copy()
                if transmission is not None:
                    if radial_gradient:
                        y_star = pceny
                        x_star = pcenx - rad
                        d = dist_matrix(size_fc, x_star, y_star)
                        for i in range(d.shape[0]):
                            fc_fr_rad[:, i] = interp_trans(d[i])*fc_fr[:, i]

                        # check the effect of transmission on a single PSF tmp
                        psf_trans = frame_rotate(fc_fr_rad[0],
                                                 -(ang*180/np.pi-angle_list[0]),
                                                 imlib=imlib_rot,
                                                 interpolation=interpolation)

                        # shift_y = rad * np.sin(ang - np.deg2rad(angle_list[0]))
                        # shift_x = rad * np.cos(ang - np.deg2rad(angle_list[0]))
                        # dsy = shift_y-int(shift_y)
                        # dsx = shift_x-int(shift_x)
                        # fc_fr_ang = frame_shift(psf_trans, dsy, dsx, imlib_sh,
                        #                         interpolation,
                        #                         border_mode='constant')
                    else:
                        fc_fr_rad = interp_trans(rad)*fc_fr
                if nproc == 1:
                    for fr in range(nframes):
                        array_out[fr] += _frame_shift_fcp(fc_fr_rad[fr],
                                                          array[fr], rad, ang,
                                                          angle_list[fr],
                                                          flevel[fr], size_fc,
                                                          imlib_sh, imlib_rot,
                                                          interpolation,
                                                          transmission,
                                                          radial_gradient)
                else:
                    res = pool_map(nproc, _frame_shift_fcp, iterable(fc_fr_rad),
                                   iterable(array), rad, ang,
                                   iterable(angle_list), iterable(flevel),
                                   size_fc, imlib_sh, imlib_rot, interpolation,
                                   transmission, radial_gradient)
                    array_out += np.array(res)

                pos_y = rad * np.sin(ang) + ceny
                pos_x = rad * np.cos(ang) + cenx

                positions.append((pos_y, pos_x))

                if verbose:
                    rad_arcs = rad * plsc
                    print('\t(X,Y)=({:.2f}, {:.2f}) at {:.2f} arcsec '
                          '({:.2f} pxs from center)'.format(pos_x, pos_y,
                                                            rad_arcs, rad))

        return array_out, positions, psf_trans

    check_array(array, dim=(3, 4), msg="array")
    check_array(psf_template, dim=(2, 3), msg="psf_template")

    nframes = array.shape[-3]

    pceny, pcenx = frame_center(psf_template)

    if array.ndim == 4 and psf_template.ndim != 3:
        raise ValueError('`psf_template` must be a 3d array')

    if verbose and not np.isscalar(plsc):
        raise TypeError("`plsc` must be a scalar")
    if not np.isscalar(flevel):
        if len(flevel) != array.shape[0]:
            msg = "if not scalar `flevel` must have same length as array"
            raise TypeError(msg)

    # set imlib for rotation & shift (rotation used if transmission!=None)
    if imlib == 'opencv':
        imlib_sh = imlib
        imlib_rot = imlib
    elif imlib == 'skimage' or imlib == 'ndimage-interp':
        imlib_sh = 'ndimage-interp'
        imlib_rot = 'skimage'
    elif imlib == 'vip-fft' or imlib == 'ndimage-fourier':
        imlib_sh = imlib
        imlib_rot = 'vip-fft'
    else:
        raise TypeError("Interpolation not recognized.")

    rad_dists = np.asarray(rad_dists).reshape(-1)  # forces ndim=1

    if not rad_dists[-1] < array.shape[-1] / 2:
        raise ValueError('rad_dists last location is at the border (or '
                         'outside) of the field')

    if transmission is not None:
        t_nz = transmission.shape[0]
        if transmission.ndim != 2:
            raise ValueError("transmission should be a 2D ndarray")
        elif t_nz != 2 and t_nz != 1+array.shape[0]:
            msg = "transmission dimensions should be either (2,N) or (n_wave+1, N)"
            raise ValueError(msg)
        # if transmission doesn't have right format for interpolation, adapt it
        diag = np.sqrt(2)*array.shape[-1]
        if transmission[0, 0] != 0 or transmission[0, -1] < diag:
            trans_rad_list = transmission[0].tolist()
            for j in range(t_nz-1):
                trans_list = transmission[j+1].tolist()
                # should have a zero point
                if transmission[0, 0] != 0:
                    if j == 0:
                        trans_rad_list = [0]+trans_rad_list
                    trans_list = [0]+trans_list
                # last point should be max possible distance between fc and star
                if transmission[0, -1] < np.sqrt(2)*array.shape[-1]/2:
                    if j == 0:
                        trans_rad_list = trans_rad_list+[diag]
                    trans_list = trans_list+[1]
                if j == 0:
                    ntransmission = np.zeros([t_nz, len(trans_rad_list)])
                    ntransmission[0] = trans_rad_list
                ntransmission[j+1] = trans_list
            transmission = ntransmission.copy()

    # ADI case
    if array.ndim == 3:
        res = _cube_inject_adi(array, psf_template, angle_list, flevel, plsc,
                               rad_dists, n_branches, theta, imlib,
                               interpolation, transmission, radial_gradient,
                               verbose)
        array_out, positions, psf_trans = res

    # ADI+mSDI (IFS) case
    else:
        nframes_wav = array.shape[0]
        array_out = array.copy()
        if np.isscalar(flevel):
            flevel_all = np.ones([nframes_wav, nframes])*flevel
        elif flevel.ndim == 1:
            flevel_all = np.zeros([nframes_wav, nframes])
            for i in range(nframes_wav):
                flevel_all[i, :] = flevel[i]
        else:
            flevel_all = flevel
        for i in range(nframes_wav):
            if verbose:
                msg = "*** Processing spectral channel {}/{} ***"
                print(msg.format(i+1, nframes_wav))
            if transmission is None:
                trans = None
            elif transmission.shape[0] == 2:
                trans = transmission
            elif transmission.shape[0] == nframes_wav+1:
                trans = np.array([transmission[0], transmission[i+1]])
            else:
                msg = "transmission shape ({}, {}) is not valid"
                raise TypeError(msg.format(transmission.shape[0],
                                           transmission.shape[1]))
            res = _cube_inject_adi(array[i], psf_template[i], angle_list,
                                   flevel_all[i], plsc, rad_dists, n_branches,
                                   theta, imlib, interpolation, trans,
                                   radial_gradient,
                                   verbose=(i == 0 & verbose is True))
            array_out[i], positions, psf_trans = res

    if full_output:
        if transmission is not None:
            return array_out, positions, psf_trans
        else:
            return array_out, positions
    else:
        return array_out

def _frame_shift_fcp(fc_fr_rad, array, rad, ang, derot_ang, flevel, size_fc,
                     imlib_sh, imlib_rot, interpolation, transmission,
                     radial_gradient):
    """
    Specific cube shift algorithm for injection of fake companions
    """

    ceny, cenx = frame_center(array)
    sizey = array.shape[-2]
    sizex = array.shape[-1]

    array_sh = np.zeros_like(array)

    w = int(np.ceil(size_fc/2))
    if size_fc % 2:  # new convention
        w -= 1
    sty = int(ceny) - w
    stx = int(cenx) - w

    shift_y = rad * np.sin(ang - np.deg2rad(derot_ang))
    shift_x = rad * np.cos(ang - np.deg2rad(derot_ang))
    if transmission is not None and radial_gradient:
        fc_fr_ang = frame_rotate(fc_fr_rad, -(ang*180/np.pi - derot_ang),
                                 imlib=imlib_rot, interpolation=interpolation)
    else:
        fc_fr_ang = fc_fr_rad.copy()

    # sub-px shift (within PSF template frame)
    dsy = shift_y-int(shift_y)
    dsx = shift_x-int(shift_x)
    fc_fr_ang = frame_shift(fc_fr_ang, dsy, dsx, imlib_sh, interpolation,
                            border_mode='constant')
    # integer shift (in final cube)
    y0 = sty+int(shift_y)
    x0 = stx+int(shift_x)
    yN = y0+size_fc
    xN = x0+size_fc
    p_y0 = 0
    p_x0 = 0
    p_yN = size_fc
    p_xN = size_fc
    if y0 < 0:
        p_y0 = -y0
        y0 = 0
    if x0 < 0:
        p_x0 = -x0
        x0 = 0
    if yN > sizey:
        p_yN -= yN-sizey
        yN = sizey
    if xN > sizex:
        p_xN -= xN-sizex
        xN = sizex

    array_sh[y0:yN, x0:xN] = flevel*fc_fr_ang[p_y0:p_yN, p_x0:p_xN]

    return array_sh

def generate_cube_copies_with_injections(array, psf_template, angle_list, plsc,
                                         n_copies=100, inrad=8, outrad=12,
                                         dist_flux=("uniform", 2, 500)):
    """
    Create multiple copies of ``array`` with different random injections.

    This is a wrapper around ``metrics.cube_inject_companions``, which deals
    with multiple copies of the original data cube and generates random
    parameters.

    Parameters
    ----------
    array : 3d/4d numpy ndarray
        Original input cube.
    psf_template : 2d/3d numpy ndarray
        Array with the normalized psf template. It should have an odd shape.
        It's recommended to run the function ``normalize_psf`` to get a proper
        PSF template. In the ADI+mSDI case it must be a 3d array.
    angle_list : 1d numpy ndarray
        List of parallactic angles, in degrees.
    plsc : float
        Value of the plsc in arcsec/px. Only used for printing debug output when
        ``verbose=True``.
    n_copies : int
        This is the number of 'cube copies' returned.
    inrad,outrad : float
        Inner and outer radius of the injections. The actual injection position
        is chosen randomly.
    dist_flux : tuple('method', params)
        Tuple describing the flux selection. Method can be a function, the
        ``*params`` are passed to it. Method can also be a string, for a
        pre-defined random function:

            ``('skewnormal', skew, mean, var)``
                uses scipy.stats.skewnorm.rvs
            ``('uniform', low, high)``
                uses np.random.uniform
            ``('normal', loc, scale)``
                uses np.random.normal

    Returns
    -------
    fake_data : dict
        Represents a copy of the original ``array``, with fake injections. The
        dictionary keys are:

            ``cube``
                Array shaped like the input ``array``, with the fake injections.
            ``position`` : list of tuples(y,x)
                List containing the positions of the injected companions, as
                (y,x) tuples.
            ``dist`` : float
                The distance of the injected companions, which was passed to
                ``cube_inject_companions``.
            ``theta`` : float, degrees
                The initial angle, as passed to ``cube_inject_companions``.
            ``flux`` : float
                The flux passed to ``cube_inject_companions``.

    """
    # TODO: 'mask' parameter for known companions?

    width = outrad - inrad
    yy, xx = get_annulus_segments(array[0], inrad, width)[0]
    num_patches = yy.shape[0]

    # Defining Fluxes according to chosen distribution
    dist_fkt = dict(skewnormal=stats.skewnorm.rvs,
                    normal=np.random.normal,
                    uniform=np.random.uniform).get(dist_flux[0],
                                                   dist_flux[0])
    fluxes = sorted(dist_fkt(*dist_flux[1:], size=n_copies))

    inds_inj = np.random.randint(0, num_patches, size=n_copies)

    # Injections
    for n in range(n_copies):

        injx = xx[inds_inj[n]] - frame_center(array[0])[1]
        injy = yy[inds_inj[n]] - frame_center(array[0])[0]
        dist = np.sqrt(injx**2 + injy**2)
        theta = np.mod(np.arctan2(injy, injx) / np.pi * 180, 360)

        fake_cube, positions = cube_inject_companions(
            array, psf_template, angle_list, plsc=plsc,
            flevel=fluxes[n], theta=theta,
            rad_dists=dist, n_branches=1,  # TODO: multiple injections?
            full_output=True, verbose=False
        )

        yield dict(
            positions=positions,
            dist=dist, theta=theta, flux=fluxes[n],
            cube=fake_cube
        )

def frame_inject_companion(array, array_fc, pos_y, pos_x, flux,
                           imlib='vip-fft', interpolation='lanczos4'):
    """
    Injects a fake companion in a single frame (it could be a single
    multi-wavelength frame) at given coordinates, or in a cube (at the same
    coordinates, flux and with same fake companion image throughout the cube).

    Parameters
    ----------
    array : numpy ndarray, 2d or 3d
        Input frame or cube.
    array_fc : numpy ndarray, 2d
        Fake companion image to be injected. If even-dimensions, the center
        should be placed at coordinates [dim//2, dim//2] (0-based indexing),
        as per VIP's convention.
    pos_y, pos_x: float
         Y and X coordinates where the companion should be injected
    flux : int
        Flux at which the fake companion should be injected (i.e. scaling
        factor for the injected image)
    imlib : str, optional
        See documentation of the ``vip_hci.preproc.frame_shift`` function.
    interpolation : str, optional
        See documentation of the ``vip_hci.preproc.frame_shift`` function.

    Returns
    -------
    array_out : numpy ndarray
        Frame or cube with the companion injected
    """
    if not (array.ndim == 2 or array.ndim == 3):
        raise TypeError('Array is not a 2d or 3d array.')
    if array.ndim == 2:
        size_fc = array_fc.shape[0]
        ceny, cenx = frame_center(array)
        ceny = int(ceny)
        cenx = int(cenx)
        fc_fr = np.zeros_like(array)
        w = int(np.floor(size_fc/2.))
        odd = size_fc % 2
        # fake companion in the center of a zeros frame
        fc_fr[ceny-w:ceny+w+odd, cenx-w:cenx+w+odd] = array_fc
        array_out = array + frame_shift(fc_fr, pos_y-ceny, pos_x-cenx, imlib,
                                        interpolation) * flux

    if array.ndim == 3:
        size_fc = array_fc.shape[1]
        ceny, cenx = frame_center(array[0])
        ceny = int(ceny)
        cenx = int(cenx)
        fc_fr = np.zeros_like(array)
        w = int(np.floor(size_fc/2.))
        odd = size_fc % 2
        # fake companion in the center of a zeros frame
        fc_fr[:, ceny-w:ceny+w+odd, cenx-w:cenx+w+odd] = array_fc
        array_out = array + cube_shift(fc_fr, pos_y - ceny, pos_x - cenx,
                                       imlib, interpolation) * flux

    return array_out

def collapse_psf_cube(array, size, fwhm=4, verbose=True, collapse='mean'):
    """ Creates a 2d PSF template from a cube of non-saturated off-axis frames
    of the star by taking the mean and normalizing the PSF flux.

    Parameters
    ----------
    array : numpy ndarray, 3d
        Input cube.
    size : int
        Size of the squared subimage.
    fwhm: float, optional
        The size of the Full Width Half Maximum in pixel.
    verbose : {True,False}, bool optional
        Whether to print to stdout information about file opening, cropping and
        completion of the psf template.
    collapse : {'mean','median'}, string optional
        Defines the way the frames are collapsed.

    Returns
    -------
    psf_normd : numpy ndarray
        Normalized PSF.
    """
    if array.ndim != 3 and array.ndim != 4:
        raise TypeError('Array is not a cube, 3d or 4d array.')

    n = array.shape[0]
    psf = cube_crop_frames(array, size=size, verbose=verbose)
    if collapse == 'mean':
        psf = np.mean(psf, axis=0)
    elif collapse == 'median':
        psf = np.median(psf, axis=0)
    else:
        raise TypeError('Collapse mode not recognized.')

    psf_normd = normalize_psf(psf, size=size, fwhm=fwhm)

    if verbose:
        print("Done scaled PSF template from the average of", n, "frames.")
    return psf_normd

def normalize_psf(array, fwhm='fit', size=None, threshold=None, mask_core=None,
                  model='gauss', imlib='vip-fft', interpolation='lanczos4',
                  force_odd=True, correct_outliers=True, full_output=False,
                  verbose=True, debug=False):
    """ Normalizes a PSF (2d or 3d array), to have the flux in a 1xFWHM
    aperture equal to one. It also allows to crop the array and center the PSF
    at the center of the array(s).

    Parameters
    ----------
    array: numpy ndarray
        The PSF, 2d (ADI data) or 3d array (IFS data).
    fwhm: int, float, 1d array or str, optional
        The Full Width Half Maximum in pixels. It can handle a different
        FWHM value for different wavelengths (IFS data). If set to 'fit' then
        a ``model`` (assuming the PSF is centered in the array) is fitted to
        estimate the FWHM in 2D or 3D PSF arrays.
    size : int or None, optional
        If int it will correspond to the size of the centered sub-image to be
        cropped form the PSF array. The PSF is assumed to be roughly centered wrt
        the array.
    threshold : None or float, optional
        Sets to zero values smaller than threshold (in the normalized image).
        This can be used to only leave the core of the PSF.
    mask_core : None or float, optional
        Sets the radius of a circular aperture for the core of the PSF,
        everything else will be set to zero.
    model : {'gauss', 'moff', 'airy'}, str optional
        The assumed model used to fit the PSF: either a Gaussian, a Moffat
        or an Airy 2d distribution.
    imlib : str, optional
        See the documentation of the ``vip_hci.preproc.frame_shift`` function.
    interpolation : str, optional
        See the documentation of the ``vip_hci.preproc.frame_shift`` function.
    force_odd : bool, optional
        If True the resulting array will have odd size (and the PSF will be
        placed at its center). If False, and the frame size is even, then the
        PSF will be put at the center of an even-sized frame.
    correct_outliers: bool, optional
        For an input 3D cube (IFS) of PSFs, if the 2D fit fails for one of the
        channels, whether to interpolate FWHM value from surrounding channels,
        and recalculate flux and normalization.
    full_output : bool, optional
        If True the flux in a FWHM aperture is returned along with the
        normalized PSF.
    verbose : bool, optional
        If True intermediate results are printed out.
    debug : bool, optional
        If True the fitting will output additional information and a diagnostic
        plot will be shown (this might cause a long output if ``array`` is 3d
        and has many slices).

    Returns
    -------
    psf_norm : numpy ndarray
        The normalized PSF (2d or 3d array).
    fwhm_flux : numpy ndarray
        [full_output=True] The flux in a FWHM aperture (it can be a single
        value or a vector).
    fwhm : numpy ndarray
        [full_output=True] The FWHM size. If ``fwhm`` is set to 'fit' then it
        is the fitted FWHM value according to the assumed ``model`` (the mean in
        X and Y is returned when ``model`` is set to 'gauss').

    """
    def psf_norm_2d(psf, fwhm, threshold, mask_core, full_output, verbose):
        """ 2d case """
        # we check if the psf is centered and fix it if needed
        cy, cx = frame_center(psf, verbose=False)
        xcom, ycom = cen_com(psf)
        if not (np.allclose(cy, ycom, atol=1e-2) or
                np.allclose(cx, xcom, atol=1e-2)):
            # first we find the centroid and put it in the center of the array
            centry, centrx = fit_2d(psf, full_output=False, debug=False)
            if not np.isnan(centry) and not np.isnan(centrx):
                shiftx, shifty = centrx - cx, centry - cy
                psf = frame_shift(psf, -shifty, -shiftx, imlib=imlib,
                                  interpolation=interpolation)

                for _ in range(2):
                    centry, centrx = fit_2d(psf, full_output=False, debug=False)
                    if np.isnan(centry) or np.isnan(centrx):
                        break
                    cy, cx = frame_center(psf, verbose=False)
                    shiftx, shifty = centrx - cx, centry - cy
                    psf = frame_shift(psf, -shifty, -shiftx, imlib=imlib,
                                      interpolation=interpolation)

        # we check whether the flux is normalized and fix it if needed
        fwhm_aper = CircularAperture((cx, cy), fwhm/2)
        fwhm_aper_phot = aperture_photometry(psf, fwhm_aper,
                                             method='exact')
        fwhm_flux = np.array(fwhm_aper_phot['aperture_sum'])

        if fwhm_flux > 1.1 or fwhm_flux < 0.9:
            psf_norm_array = psf / np.array(fwhm_aper_phot['aperture_sum'])
        else:
            psf_norm_array = psf

        if threshold is not None:
            psf_norm_array[np.where(psf_norm_array < threshold)] = 0

        if mask_core is not None:
            psf_norm_array = get_circle(psf_norm_array, radius=mask_core)

        if verbose:
            print("Flux in 1xFWHM aperture: {:.3f}".format(fwhm_flux[0]))

        if full_output:
            return psf_norm_array, fwhm_flux, fwhm
        else:
            return psf_norm_array
    ###########################################################################
    if model == 'gauss':
        fit_2d = fit_2dgaussian
    elif model == 'moff':
        fit_2d = fit_2dmoffat
    elif model == 'airy':
        fit_2d = fit_2dairydisk
    else:
        raise ValueError('`Model` not recognized')

    if array.ndim == 2:
        y, x = array.shape
        if size is not None:
            if force_odd and size % 2 == 0:
                size += 1
                msg = "`Force_odd` is True therefore `size` was set to {}"
                print(msg.format(size))
        else:
            if force_odd and y % 2 == 0:
                size = y - 1
                msg = "`Force_odd` is True and frame size is even, therefore "
                msg += "new frame size was set to {}"
                print(msg.format(size))

        if size is not None:
            if size < array.shape[0]:
                array = frame_crop(array, size, force=True, verbose=False)
            else:
                array = array.copy()
        else:
            array = array.copy()

        if not np.isscalar(fwhm):
            msg = "For a 2d input array, fwhm should be a scalar or string."
            raise ValueError(msg)
        elif fwhm == 'fit':
            fit = fit_2d(array, full_output=True, debug=debug)
            if model == 'gauss':
                fwhm = np.mean((fit['fwhm_x'], fit['fwhm_y']))
                if verbose:
                    print("\nMean FWHM: {:.3f}".format(fwhm))
            elif model == 'moff' or model == 'airy':
                fwhm = fit.fwhm.at[0]
                if verbose:
                    print("FWHM: {:.3f}".format(fwhm))

        res = psf_norm_2d(array, fwhm, threshold, mask_core, full_output,
                          verbose)
        return res

    elif array.ndim == 3:
        n, y, x = array.shape
        if size is not None:
            if force_odd and size % 2 == 0:
                size += 1
                msg = "`Force_odd` is True therefore `size` was set to {}"
                print(msg.format(size))
        else:
            if force_odd and y % 2 == 0:
                size = y - 1
                msg = "`Force_odd` is True and frame size is even, therefore "
                msg += "new frame size was set to {}"
                print(msg.format(size))

        if size is not None:
            if size < array.shape[1]:
                array = cube_crop_frames(array, size, force=True, verbose=False)
            else:
                array = array.copy()

        if np.isscalar(fwhm):
            if fwhm != 'fit':
                fwhm = [fwhm] * array.shape[0]
            else:
                fits_vect = [fit_2d(array[i], full_output=True, debug=debug) for i
                             in range(n)]
                if model == 'gauss':
                    fwhmx = [fits_vect[i]['fwhm_x'] for i in range(n)]
                    fwhmy = [fits_vect[i]['fwhm_y'] for i in range(n)]
                    fwhm_vect = [np.mean((fwhmx[i], fwhmy[i]))
                                 for i in range(n)]
                    fwhm = np.array(fwhm_vect)
                    if verbose:
                        print("Mean FWHM per channel: ")
                        print_precision(fwhm)
                elif model == 'moff' or model == 'airy':
                    fwhm_vect = [fits_vect[i]['fwhm'] for i in range(n)]
                    fwhm = np.array(fwhm_vect)
                    fwhm = fwhm.flatten()
                    if verbose:
                        print("FWHM per channel:")
                        print_precision(fwhm)
                # Replace outliers if needed
                if correct_outliers:
                    if np.sum(np.isnan(fwhm)) > 0:
                        for f in range(n):
                            if np.isnan(fwhm[f]) and f != 0 and f != n-1:
                                fwhm[f] = np.nanmean(np.array([fwhm[f-1],
                                                               fwhm[f+1]]))
                            elif np.isnan(fwhm[f]):
                                msg = "2D fit failed for first or last channel. "
                                msg += "Try other parameters?"
                                raise ValueError(msg)
        elif len(fwhm) != array.shape[0]:
            msg = "If fwhm is a list/1darray it should have a length of {}"
            raise ValueError(msg.format(array.shape[0]))

        array_out = []
        fwhm_flux = np.zeros(n)

        for fr in range(array.shape[0]):
            restemp = psf_norm_2d(array[fr], fwhm[fr], threshold, mask_core,
                                  True, False)
            array_out.append(restemp[0])
            fwhm_flux[fr] = restemp[1]

        array_out = np.array(array_out)
        if verbose:
            print("Flux in 1xFWHM aperture: ")
            print_precision(fwhm_flux)

        if full_output:
            return array_out, fwhm_flux, fwhm
        else:
            return array_out


"""Estimation of number of seats in CZ parliament based on pollsters' potentials."""
# !!! NOTE: HTML in /home/michal/dev/dark_corners/flow/test4/

import copy
import csv
import datetime
import json
import numpy
import operator

path = "/home/michal/project/mandaty.cz/seats/"

# date = "2019-10-01"
datestring = datetime.datetime.today().strftime('%Y-%m-%d')
inputfile = "mandaty.csv"
# inputfile = "party_sociologu201706_kdustan.csv"
outputfile = "stats.json"
outputrichfile = "current_seats.json"
lastresultsfile = "psp2017_results_selected.csv"
lastregionalfile = "psp2017_selected.csv"
lastseats = "psp2017_seats.csv"
coalitionsinfile = "coalitions.csv"
coalitionsoutfile = "current_coalitions.json"
n = 770
runs = 100

def dhondt(parties, seats):
    """D'Hondt."""
    line = []
    for k in parties:
        for j in range(1, seats + 1):
            line.append({
                'party_code': k,
                'value': parties[k] / j
            })
    line_sorted = sorted(line, key=lambda x: x['value'], reverse=True)
    nof_seats = {}
    for k in parties:
        nof_seats[k] = 0
    for j in range(0, seats):
        nof_seats[line_sorted[j]['party_code']] += 1
    return nof_seats

trial_seats = {}
# current data / polls
current_poll = {}
with open(path + inputfile) as fin:
    dr = csv.DictReader(fin)
    for row in dr:
        current_poll[row['party_code']] = row

# gains 2017
gains_prev = {}
with open(path + lastresultsfile) as fin:
    dr = csv.DictReader(fin)
    for row in dr:
        gains_prev[row['party_code']] = row

# votes 2017
votes_prev = {}
with open(path + lastregionalfile) as fin:
    dr = csv.DictReader(fin)
    for row in dr:
        try:
            votes_prev[row['party_code']]
        except Exception:
            votes_prev[row['party_code']] = {}
        votes_prev[row['party_code']][row['region_code']] = row

# votes 2017 totals
votes_totals_prev = {}
for k in votes_prev:
    s = 0
    for j in votes_prev[k]:
        s += int(votes_prev[k][j]['votes'])
    votes_totals_prev[k] = s

# seats in regions
regions_seats = {}
with open(path + lastseats) as fin:
    dr = csv.DictReader(fin)
    for row in dr:
        regions_seats[row['region_code']] = row

def calculate_seats(current):
    """Calculate seats from poll."""
    # calculated no of votes
    calc_nof_votes = {}
    for k in current:
        party_code_prev = current[k]['party_code_2017']
        if float(current[k]['gain']) >= float(current[k]['needs']):
            calc_nof_votes[k] = float(current[k]['gain']) / float(gains_prev[party_code_prev]['gain']) * votes_totals_prev[party_code_prev]
        else:
            calc_nof_votes[k] = 0

    # calculated no of votes by regions
    calc_nof_votes_regions = {}
    for k in calc_nof_votes:
        party_code_prev = current[k]['party_code_2017']
        for j in regions_seats:
            try:
                calc_nof_votes_regions[j]
            except Exception:
                calc_nof_votes_regions[j] = {}
            calc_nof_votes_regions[j][k] = calc_nof_votes[k] / votes_totals_prev[party_code_prev] * int(votes_prev[party_code_prev][j]['votes'])

    # calculate total seats
    calc_seats = {}
    for k in current:
        calc_seats[k] = 0

    for j in regions_seats:
        calculated = dhondt(calc_nof_votes_regions[j], int(regions_seats[j]['seats']))
        # if j == 'pa':
        #     print(j, calculated['pirati'])
        for k in current:
            calc_seats[k] += calculated[k]

    return calc_seats

for i in range(0, runs):
    # randomize sample
    current = copy.deepcopy(current_poll)
    for k in current_poll:
        current[k]['gain'] = numpy.random.binomial(n, float(current[k]['gain'])) / n

    calc_seats = calculate_seats(current)

    for k in calc_seats:
        try:
            trial_seats[k]
        except Exception:
            trial_seats[k] = []
        trial_seats[k].append(calc_seats[k])

seats = calculate_seats(current_poll)

stats = []
for k in trial_seats:
    try:
        difference = seats[k] - int(gains_prev[current_poll[k]['party_code_2017']]['seats'])
        # difference = seats[k] - int(current_poll[k]['seats_2017'])
    except Exception:
        difference = seats[k]
    row = {
        'party_code': k,
        'median': sorted(trial_seats[k])[round(runs * 0.5)],
        'lo': sorted(trial_seats[k])[round(runs * 0.05)],
        'hi': sorted(trial_seats[k])[round(runs * 0.95)],
        'seats': seats[k],
        'difference': difference,
        'name': current_poll[k]['name'],
        'color': current_poll[k]['color'],
        'gain': current_poll[k]['gain']
    }
    print(row)
    stats.append(row)

stats.sort(key=operator.itemgetter("gain"), reverse=True)
stats.sort(key=operator.itemgetter("hi"), reverse=True)
stats.sort(key=lambda x: x['seats'], reverse=True)
with open(path + outputfile, "w") as fout:
    json.dump(stats[0:9], fout)

with open(path + outputrichfile, "w") as fout:
    rich = {
        "data": stats[0:9],
        "date": datestring
    }
    json.dump(rich, fout, ensure_ascii=False)

def majority_probability(seats, majority=101):
    """Probability of majority."""
    over = 0
    for i in range(0, runs):
        s = 0
        for k in seats:
            s += seats[k][i]
        if s >= majority:
            over += 1
    return over / runs

def coalition_seats(trial_seats, party_codes):
    """Calculate the number of seats for a coalition."""
    ss = []
    for run in range(0, runs):
        sm = 0
        for code in party_codes:
            sm += trial_seats[code][run]
        ss.append(sm)
    return ss

# coalitions
coalitions = []
potentials = []
# single party
for k in trial_seats:
    potentials.append({
        'trial_seats': {k: trial_seats[k]},
        'seats': seats[k],
        'party_code': k,
        'keys': [k]
    })
# two parties
for k in trial_seats:
    for m in trial_seats:
        if k < m:
            potentials.append({
                'trial_seats': {k: trial_seats[k], m: trial_seats[m]},
                'seats': sorted(coalition_seats(trial_seats, [k, m]))[round(runs * 0.5)],
                'party_code': '+'.join([k, m]),
                'keys': [k, m]
            })
# multiple parties
with open(path + coalitionsinfile) as fin:
    dr = csv.DictReader(fin)
    for row in dr:
        keys = row['party_code'].split('+')
        ts = {}
        for key in keys:
            ts[key] = trial_seats[key]
        potentials.append({
            'trial_seats': ts,
            'seats': sorted(coalition_seats(trial_seats, keys))[round(runs * 0.5)],
            'party_code': row['party_code'],
            'keys': keys
        })

# colors and names
code2data = {}
for s in stats:
    code2data[s['party_code']] = {
        'name': s['name'],
        'color': s['color']
    }

# try potentials
for p in potentials:
    mp = majority_probability(p['trial_seats'])
    if mp > 0:
        item = {
            'party_code': p['party_code'],
            'seats': p['seats'],
            'majority_probability': mp,
            'party_codes': p['keys']
        }
        coalitions.append(item)
coalitions.sort(key=lambda x: x['majority_probability'], reverse=True)

with open(path + coalitionsoutfile, "w") as fout:
    it = {
        "coalitions": coalitions,
        "meta": code2data,
        "date": datestring
    }
    json.dump(it, fout)

# seats for a party:
# with open(path + code + "_pirateseats.csv", "w") as fout:
#     dw = csv.DictWriter(fout, ['seats'])
#     dw.writeheader()
#     for row in trial_seats['pirati']:
#         dw.writerow({'seats': row})


from netaddr import IPAddress

__all__ = [
    'to_server_dict',
    'to_dns_zone_dict',
    'to_dns_record_dict'
]

def to_server_dict(server):
    public_ips = [ip['addr'] for ip in server.addresses['public']]
    private_ips = [ip['addr'] for ip in server.addresses['private']]

    # Pick out first public IPv4 and IPv6 address
    public_ipv4 = None
    public_ipv6 = None

    for ip in public_ips:
        try:
            ip_obj = IPAddress(ip)
        except Exception:
            continue

        if not ip_obj.is_private():
            if ip_obj.version == 4:
                public_ipv4 = ip
            elif ip_obj.version == 6:
                public_ipv6 = ip

    result = {
        'id': server.id,
        'name': server.name,
        'status': server.status,
        'image_id': server.image['id'],
        'flavor_id': server.flavor['id'],
        'public_ips': public_ips,
        'private_ips': private_ips,
        'public_ipv4': public_ipv4,
        'public_ipv6': public_ipv6,
        'key_name': server.key_name,
        'metadata': server.metadata
    }
    return result

def to_dns_zone_dict(zone):
    result = {
        'id': zone.id,
        'name': zone.name,
        'email_address': zone.emailAddress,
        'ttl': zone.ttl
    }
    return result

def to_dns_record_dict(record):
    result = {
        'id': record.id,
        'name': record.name,
        'type': record.type,
        'data': record.data,
        'ttl': record.ttl
    }
    return result


import os
import base64
from datetime import datetime

from flask import Blueprint, render_template, request, flash, redirect, url_for, current_app
from flask_login import current_user, login_user, logout_user, login_required

from microblog.models import db, User
from microblog.forms import LoginForm, RegistrationForm, ProfileForm

auth_bp = Blueprint('auth', __name__)

@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()

    if request.method == 'POST':
        if form.validate_on_submit():
            username = form.username.data
            user = User.query.filter_by(username=username).first()
            if user and user.check_password(form.password.data):
                user.last_login = datetime.utcnow()
                db.session.commit()
                login_user(user)
                if not os.path.exists(user.upload_dir):
                    os.mkdir(user.upload_dir)

                flash("{} başarılı bir giriş yaptın".format(form.username.data))
                next_page = request.args.get('next') or url_for('index.index')
                return redirect(next_page)
            else:
                flash("Kullanıcı adı ya da parola hatalı")
        else:
            flash("Aşağaıda belirtilen alanlar eksik")

    return render_template('auth/login.html', form=form)

@auth_bp.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('auth.login'))

@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        flash("Zaten kayıtlısın")
        return redirect(url_for('index.index'))

    form = RegistrationForm()

    if request.method == 'POST':
        if form.validate_on_submit():
            user = User(username=form.username.data, email=form.email.data)
            user.set_password(form.password.data)
            db.session.add(user)
            db.session.commit()
            flash("Kayıtlanma başarılı. Şimdi giril yapabilirsiniz.")
            return redirect(url_for('auth.login'))

    return render_template('auth/register.html', form=form)

@auth_bp.route('/profile/edit', methods=['GET', 'POST'])
@login_required
def profile_edit():
    form = ProfileForm()

    if request.method == 'POST':
        current_user.about_me = form.about_me.data
        current_user.email_public = form.email_public.data
        if form.avatar.data:
            avatar = form.avatar.data.stream.read()
            current_user.avatar = base64.encodebytes(avatar).decode()
        db.session.commit()
        flash("Profiliniz kaydedildi")

    else:
        form.about_me.data = current_user.about_me
        form.email_public.data = current_user.email_public

    return render_template('auth/profile_edit.html', form=form)

@auth_bp.route('/profile/show/<username>')
@login_required
def profile_show(username):
    puser = User.query.filter_by(username=username).first()
    if not puser:
        flash("Böyle bir kullanıcı mevcut değil")
        return redirect(url_for('index.index'))

    return render_template('auth/profile_show.html', puser=puser)


from django.contrib import admin
from django.contrib.admin import ModelAdmin

from backend.hume.models import Hume, ValidHume

class ValidHumeAdmin(ModelAdmin):
    model = ValidHume
    list_display = ('uuid', )

class HumeAdmin(ModelAdmin):
    model = Hume
    list_display = ('uuid', )

admin.site.register(ValidHume, ValidHumeAdmin)
admin.site.register(Hume, HumeAdmin)


import sqlite3

con = sqlite3.connect("sample.db")
cursor = con.cursor()
create_history_table_query = """
CREATE TABLE IF NOT EXISTS History(
    Name TXET,
    Age INTEGER
)
"""
cursor.execute(create_history_table_query)
target_name = input("whose 'age' do you want to update?")
new_age = input("Tell me new age:")
update_user_query = 'UPDATE User SET Age = ? WHERE Name = ?'
insert_history_query = 'INSERT INTO History VALUES (?, ?)'

try:
    cursor.execute(insert_history_query, (target_name, new_age))
    cursor.execute(update_user_query, (new_age, target_name))
except sqlite3.Error:
    print("sqlite3 error occurred")
    con.rollback()
else:
    con.commit()

# else: con.commit() があるならば、 con.rollback()はいらないじゃないか？となるが…
# rollback がないと、 error の時点では commite されず pending(ペンディング)の状態：保留という意味
# error がなくなって commite できる状態になった場合に、pending(保留)されていたものも commite されてしまう
# 必ず rollback を付ける事


import json

with open('misleading.json','r') as f:
    results = json.load(f)
"""
res = {
    'margins':list(margins),
    'min_round_sizes':list(round_sizes),
    'prov_sprobs':list(prov_sprobs),
    'so_sprobs':list(so_sprobs),
    'eor_sprobs':list(eor_sprobs)
}
"""
s = "\\begin{tabular}{ |c|c|c|c|c| } \n\\hline \n misleading & margin & $n$ & Prov & SO & EOR \\\\\n\hline\n"

for i in range(len(results['misleading_limits'])):
    misleading_limit = results['misleading_limits'][i]

    s += str(misleading_limit)

    res = results['results'][i]
    margins = res['margins']
    min_round_sizes = res['min_round_sizes']
    prov_sprobs = res['prov_sprobs']
    so_sprobs = res['so_sprobs']
    eor_sprobs = res['eor_sprobs']

    for i in range(len(margins)):
        #print('margin:'+str(margins[i]))
        #print('min round size:'+str(min_round_sizes[i]))
        #print('prov sprob:'+str(prov_sprobs[i]))
        m = str(margins[i])
        r = str(min_round_sizes[i])
        t = 1000
        p = str(int(prov_sprobs[i] * t) / t)
        so = str(int(so_sprobs[i] * t) / t)
        eor = str(int(eor_sprobs[i] * t) / t)
        #s+=str(margins[i])+'&'+str()+'&'+str(prov_sprobs[i])+'&'+str(so_sprobs[i])+'&'+str(eor_sprobs[i])+'\\\\\n'
        s+='&'+m+'&'+r+'&'+p+'&'+so+'&'+eor+'\\\\\n'

s+="\hline\n\end{tabular}"

print(s)


from django.shortcuts import render, redirect,get_object_or_404, HttpResponseRedirect
from .models import Category, Photo
from django.contrib import messages

# Create your views here.
def welcome(request):

    return render(request, 'photos/base.html')
def gallery(request):

    category = request.GET.get('category')
    if category == None:
       photos = Photo.objects.all()

    else:
        photos = Photo.objects.filter(category__name__contains=category)

    categories = Category.objects.all()
    # photos = Photo.objects.all()

    context = {'categories': categories, 'photos': photos}

    return render(request, 'photos/gallery.html', context)

def viewPhoto(request, pk):
    photo = Photo.objects.get(id=pk)
    return render(request, 'photos/photo.html', {'photo':photo})

def addPhoto(request):
    categories = Category.objects.all()

    if request.method == 'POST':
        data = request.POST
        image = request.FILES.get('image')

        if data['category'] != 'none':
            category = Category.objects.get(id=data['category'])

        elif data['category_new'] != '':
            category, created = Category.objects.get_or_create(name=data['category_new'])

        else:
            category = None

        photo = Photo.objects.create(
            category=category,
            description=data['description'],
            image = image,
        )

        messages.info(request, 'Photo added successfully!')
        return redirect('gallery')

    context = {'categories': categories }

    return render(request, 'photos/add.html',context)

def deletePhoto(request, pk):

    if request.method == 'POST':
        instance = Photo.objects.get(pk =pk)
        instance.delete()
        print('photo deleted')

        messages.info(request, 'Photo deleted successfully!')

        return redirect('gallery')

    else:
        return render(request, 'photos/photo.html')

def updatePhoto(request):
    if request == 'POST':
        replace = Photo.objects.get()
        replace.update()
        print('Photo replaced!')

    else:
        return render(request, 'photos/update.html')


import numpy as np
import torch

# # # refere to the pseudocode available at https://arxiv.org/src/1911.08265v2/anc/pseudocode.py

class Node(object):
    def __init__(self, prior: float):
        self.visit_count = 0
        self.prior = prior
        self.value_sum = 0
        self.children = {}
        self.hidden_state = 0
        self.reward = 0
        self.to_play = -1

    def expanded(self):
        return len(self.children) > 0

    def value(self) -> float:
        return 0 if self.visit_count == 0 else self.value_sum / self.visit_count

class MinMaxStats(object):
    def __init__(self):
        self.maximum = -float('inf')
        self.minimum = float('inf')

    def update(self, value: float):
        self.maximum = max(self.maximum, value)
        self.minimum = min(self.minimum, value)

    def normalize(self, value: float) -> float:
        if self.maximum > self.minimum:
            return (value - self.minimum) / (self.maximum - self.minimum)
        return value

class Monte_carlo_tree_search():
    def __init__(self, pb_c_base=19652, pb_c_init=1.25, discount=0.95, root_dirichlet_alpha=0.25, root_exploration_fraction=0.25):
        """
        Init the monte carlos tree search of muzero

        Parameters
        ----------
            pb_c_base (int): This is the base value used in the formula for
            calculating the exploration parameter (known as "Pb") in the MCTS
            algorithm. Pb determines the level of exploration that the algorithm
            should perform at each step, with a higher value resulting in more
            expl- oration and a lower value resulting in more exploitation.
            Defaults to 19652.

            pb_c_init (float): This is the initial value of the exploration
            parameter Pb. It determines the level of exp- loration that the
            algorithm should perform at the beginning of the search. Defaults to
            1.25.

            discount (float): This is the discount factor used in the MCTS al-
            gorithm. It determines the importance of future rewards relative to
            immediate rewards, with a hi- gher discount factor leading to a
            greater emphasis on long-term rewards. Defaults to 0.95.

            root_dirichlet_alpha (float): This is the alpha parameter of the
            Dirichlet distr- ibution used in the MCTS algorithm. The Dirichlet
            distribution is used to sample initial move probab- ilities at the
            root node of the search tree, with the alpha parameter controlling
            the level of explo- ration vs exploitation in the sampling process.
            Defaults to 0.25.

            root_exploration_fraction (float): This is the exploration fraction
            used in the MCTS algorithm. It determines the proportion of the
            sear- ch time that should be spent exploring the search tree, with a
            higher value resulting in more explora- tion and a lower value
            resulting in more exploitation. Defaults to 0.25.
        """

        self.reset(pb_c_base, pb_c_init, discount,
                   root_dirichlet_alpha, root_exploration_fraction)

    def reset(self, pb_c_base=19652, pb_c_init=1.25, discount=0.95, root_dirichlet_alpha=0.25, root_exploration_fraction=0.25):
        self.pb_c_base = pb_c_base
        self.pb_c_init = pb_c_init
        self.discount = discount
        self.root_dirichlet_alpha = root_dirichlet_alpha
        self.root_exploration_fraction = root_exploration_fraction
        self.node = None
        self.model = None
        self.overall_graph = []

    def generate_root_hidden_state(self, observation):

        self.root = Node(0)
        self.min_max_stats = MinMaxStats()
        self.root.hidden_state = self.model.representation_function_inference(
            observation)

    def set_root_to_play_with_the_play_number(self, observation):
        # Monte Carlo Tree Search (MCTS), the to_play variable represents the player
        # whose turn it is to make a move in the current position being considered. This
        # information is used to determine which player's score to update in the MCTS
        # tree, as well as which player's actions to consider when selecting the next
        # move to explore.

        #This configuration always assume the same player is in play.
        self.root.to_play = observation

    def generate_policy_and_value(self):
        policy, value = self.model.prediction_function_inference(
            self.root.hidden_state)
        return policy, value

    def expand_the_children_of_the_root_node(self, policy, player_to_play=0):
        for i in range(policy.shape[0]):
            for h in range(policy.shape[-1]):

                # if self.num_simulations == 0 :
                #     epsilon = np.random.uniform(low=1e-7, high=2e-7, size=1)[0]
                # else:
                #     epsilon = 0 # eps for policy

                self.root.children[h] = Node(prior=policy[i, h])
                self.root.children[h].to_play = self.root.to_play

    def back_propagate_initial_children(self, value, observation):
        for bnode in reversed([self.root]):
            bnode.value_sum += value if torch.equal(observation, bnode.to_play) else -value
            bnode.visit_count += 1
            self.min_max_stats.update(bnode.value())
            value = bnode.reward + self.discount * value

    def add_exploration_noise_at_the_root(self, train):
        if self.num_simulations == 0 :
            train = False

        if train:
            actions = list(self.root.children.keys())
            noise = np.random.dirichlet(
                [self.root_dirichlet_alpha] * len(actions))
            frac = self.root_exploration_fraction
            for a, n in zip(actions, noise):
                self.root.children[a].prior = self.root.children[a].prior * \
                    (1 - frac) + n * frac

    def initialize_history_node_searchpath_variable(self):
        history = []
        self.node = self.root
        search_path = [self.root]
        return history, search_path

    def ucb_score(self, parent, child):
        pb_c = np.log((parent.visit_count + self.pb_c_base + 1) / self.pb_c_base) + self.pb_c_init
        prior_score = (np.sqrt(parent.visit_count) *  pb_c * child.prior) / (child.visit_count + 1)
        if child.visit_count > 0:
            value_score = self.min_max_stats.normalize( child.reward + self.discount * child.value())
        else:
            value_score = 0

        return prior_score + value_score + np.random.uniform(low=1e-7, high=2e-7, size=1)[0]

    def select_child(self):
        _, action, child = max((self.ucb_score(self.node, child), action, child)
                               for action, child in self.node.children.items())
        return action, child

    def choice_node_to_expand_using_max_ucb_score(self, history, search_path):
        while self.node.expanded():
            action, self.node = self.select_child()
            history.append(action)
            search_path.append(self.node)
        return search_path[-2]

    def generate_reward_and_hidden_state(self, parent, history):
        reward, hidden_state = self.model.dynamics_function_inference(
            parent.hidden_state, history[-1])
        return reward, hidden_state

    def update_reward_and_hidden_state_for_the_chosen_node(self, reward, hidden_state):
        self.node.reward, self.node.hidden_state = reward, hidden_state

    def generate_policy_and_value_for_the_chosen_node(self, hidden_state):
        policy, value = self.model.prediction_function_inference(hidden_state)
        return policy, value

    def create_new_node_in_the_chosen_node_with_action_and_policy(self, policy, player_to_play=0):

        for i in range(policy.shape[0]):
            for h in range(policy.shape[-1]):
                self.node.children[h] = Node(prior=policy[i, h])
                self.node.children[h].to_play = self.root.to_play

    def back_propagate_and_update_min_max_bound(self, search_path, value):

        for bnode in reversed(search_path):
            bnode.value_sum += value if torch.equal(self.root.to_play, bnode.to_play) else -value
            bnode.visit_count += 1
            self.min_max_stats.update(bnode.value())
            value = bnode.reward + self.discount * value

    def softmax(self,x):
        return(np.exp(x - np.max(x)) / np.exp(x - np.max(x)).sum())

    def generate_action_and_policy_base_on_node_count(self):

        action = np.array(list(self.root.children.keys()))
        if self.num_simulations >= 1 :
            policy = np.array([self.root.children[u].visit_count for u in list(self.root.children.keys())], dtype=np.float64)
        else:
            policy = np.array([self.root.children[u].prior for u in list(self.root.children.keys())], dtype=np.float64)
        return policy, self.root, action

    def run(self, observation=None,  num_simulations=10, model=None, train=True):

        self.model = model

        self.num_simulations = num_simulations

        self.generate_root_hidden_state(observation)

        self.set_root_to_play_with_the_play_number(observation)

        policy, value = self.generate_policy_and_value()

        self.expand_the_children_of_the_root_node(policy)

        # self.back_propagate_initial_children(value, observation)

        self.add_exploration_noise_at_the_root(train)

        for _ in range(num_simulations):

            history, search_path = self.initialize_history_node_searchpath_variable()

            parent = self.choice_node_to_expand_using_max_ucb_score(
                history, search_path)

            reward, hidden_state = self.generate_reward_and_hidden_state(
                parent, history)

            self.update_reward_and_hidden_state_for_the_chosen_node(
                reward, hidden_state)

            policy, value = self.generate_policy_and_value_for_the_chosen_node(
                hidden_state)

            self.create_new_node_in_the_chosen_node_with_action_and_policy(
                policy)

            self.back_propagate_and_update_min_max_bound(search_path, value)

        policy, root, action = self.generate_action_and_policy_base_on_node_count()

        return policy, root, action


from functools import reduce

def read_input():
    with open('input') as f:
        lines = f.read().splitlines()
    return lines

def main1(right=3, down=1):
    lines = read_input()
    tree = '#'
    pos = right
    found = 0
    for line in lines[down::down]:
        if line[pos] == tree:
            found += 1
        pos = (pos + right) % len(line)
    return found

def main2():
    params = [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]
    res = [main1(*p) for p in params]
    return reduce((lambda x, y: x * y), res)

def main(task):
    return {1: main1, 2: main2}[task]()

if __name__ == '__main__':
    print(main(2))


from selenium import webdriver
from bs4 import BeautifulSoup
import time

base_url ="https://search.naver.com/search.naver?where=view&sm=tab_jum&query="
keyword=input("검색어 입력하세요 : ")

url =base_url + keyword

driver = webdriver.Chrome()

driver.get(url)
time.sleep(3)
#page불러오는데 시간이 걸리므로 time을 자주 사용하게 된다

'''
자바스크립트 기능을 사용해서 '스크롤'기능을 이용함. 현재 x축 이동은 필요없으므로
y축 이동만 2000으로 설정해서 해봄
document.body.scrollHeight사용하면 화면 가장 밑까지 스크롤 됨
반복문을 쓰면 스크롤을 몇번 반복할지 정할 수 있음
'''
for i in range(5):
    driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")
    time.sleep(2)

naver_html = driver.page_source

soup = BeautifulSoup(naver_html, "html.parser")
items = soup.select(".api_ani_send")

rank_num=1
for area in items:
    ad = area.select_one(".link_ad")
    if ad:
        # print("광고입니다")
        continue
        # continue사용하면 아래 코드는 실행x 위로 다시 돌아감

    print(f"<<{rank_num}>>")
    names = area.select_one(".sub_txt.sub_name")
    titles = area.select_one(".api_txt_lines.total_tit")
    print(names.text)
    print(titles.text)
    print(titles['href'])
    print()

    rank_num+= 1


# SPDX-License-Identifier: MIT

from __future__ import annotations

from sqlalchemy.types import (
    INT,
    TypeDecorator,
    UserDefinedType,
)

from ..data.cvss import CvssSeverity

class CvssSeverityType(TypeDecorator):
    cache_ok = True
    impl = INT

    def process_bind_param(self, value: CvssSeverity | None, dialect) -> int | None:
        if value is not None:
            return value.value
        return None

    def process_result_value(self, value: int | None, dialect) -> CvssSeverity | None:
        if value is not None:
            return CvssSeverity(value)
        return None

class DebVersion(UserDefinedType):
    cache_ok = True

    def get_col_spec(self, **kw) -> str:
        return 'debversion'


import os
import json
import shutil
import argparse
import datetime
from dataclasses import dataclass, asdict
from dacite import from_dict
@dataclass
class Asset:
    m_FileID: int
    m_PathID: int

@dataclass
class OneLiner:
    characterProgressTokens: list[Asset]
    worldProgressTokens: list[Asset]
    priority: int
    text: list[Asset]

@dataclass
class DialogueCycle:
    relationshipRequirement: int
    text: Asset
    oneLiners: list[OneLiner]

@dataclass
class NPCDialogueCyclesWithText:
    npc_name: str
    married_cycles: list[str]
    dating_cycles: list[str]
    general_cycles: list[str] # note these are used for dating too if you finish the dating cycles
    one_liners: list[list[str]] # one liners have requirements that I'm not extracting yet, but the progress tokens + priority should tell you when they get used

@dataclass
class NPCDialogueCycles:
    npc_name: str
    married_cycles: list[DialogueCycle]
    dating_cycles: list[DialogueCycle]
    general_cycles: list[DialogueCycle] # note these are used for dating too if you finish the dating cycles
    one_liners: list[OneLiner] # one liners have requirements that I'm not extracting yet, but the progress tokens + priority should tell you when they get used

    def process_assets(self, assets: dict[int, str]) -> NPCDialogueCyclesWithText:
        married_cycles_text = []
        for cycle in self.married_cycles:
            text = assets.get(cycle.text.m_PathID, "")
            married_cycles_text.append(text)
        date_cycles_text = []
        for cycle in self.dating_cycles:
            text = assets.get(cycle.text.m_PathID, "")
            date_cycles_text.append(text)
        general_cycles_text = []
        for cycle in self.general_cycles:
            text = assets.get(cycle.text.m_PathID, "")
            general_cycles_text.append(text)
        oneliner_text = []
        for liner in self.one_liners:
            lines = []
            for t in liner.text:
                text = assets.get(t.m_PathID, "")
                lines.append(text)
            oneliner_text.append(lines)
        return NPCDialogueCyclesWithText(
            npc_name=self.npc_name,
            married_cycles=married_cycles_text,
            dating_cycles=date_cycles_text,
            general_cycles=general_cycles_text,
            one_liners=oneliner_text
        )

def generate(extracted_folder : str, destination_folder : str, npc: str):
    npc_cycles: dict[str, NPCDialogueCycles] = {}
    npc_text: dict[str, dict[int, str]] = {}
    npc = npc.lower()
    for file in os.listdir(extracted_folder):
        file_path = os.path.join(extracted_folder, file)

        if npc != "" and not file.startswith(npc):
            continue
        if file.endswith(".json"):
            npcai = {}
            with open(file_path, "r") as f:
                npcai = json.load(f)
            npc_name = npcai["_npcName"].lower()
            dialogue_cycles = [from_dict(DialogueCycle, x) for x in npcai["_dialogueCycles"]]
            dating_cycles =[from_dict(DialogueCycle, x) for x in npcai["_datingCycles"]]
            married_cycles = [from_dict(DialogueCycle, x) for x in npcai["_marriedCycles"]]
            one_liners = [from_dict(OneLiner, x) for x in npcai["_oneLiners"]]
            data = NPCDialogueCycles(
                npc_name=npc_name,
                married_cycles=married_cycles,
                dating_cycles=dating_cycles,
                general_cycles=dialogue_cycles,
                one_liners=one_liners
            )
            npc_cycles[npc_name] = data
        elif file.endswith(".txt"):
            parts = file.split("_")
            npc_name = parts[0]
            path_id = int(parts[1])
            with open(file_path, "rt", encoding="utf-8") as f:
                contents = f.read()
                if npc_name not in npc_text:
                    npc_text[npc_name] = {}
                npc_text[npc_name][path_id] = contents

    for (npc_name, data) in npc_cycles.items():
        # find the text files
        print(f"Processing {npc_name}")
        assets = npc_text.get(npc_name, None)
        if assets is None:
            continue
        x = data.process_assets(assets=assets)
        file_path = os.path.join(destination_folder, f"{npc_name}_cycles.json")
        with open(file_path, "w") as f:
            f.write(json.dumps(asdict(x)))

# generate jsons for each npc for passing to the ai
if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    date_time = datetime.datetime.now().strftime("%m%d%Y-%H%M%S")
    parser.add_argument("-o", "--output", type=str, default=f"generate-{date_time}", help="Output directory. If this folder already exists, it will be deleted!")
    parser.add_argument("-i", "--input", type=str, help="Extracted folder with all dialogue and npcai json")
    parser.add_argument("-n", "--npc", type=str, default="", help="If provided, only extract for the given npc. Else extract all npcs.")

    args = parser.parse_args()

    folder = args.output
    if os.path.exists(folder):
        print(f"Folder {folder} already exists, deleting...")
        shutil.rmtree(folder)
    os.mkdir(folder)
    generate(extracted_folder=args.input, destination_folder=folder, npc=args.npc)


#coding=utf-8
from selenium import webdriver
import time
import random
from PIL import Image
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By

'''
for i in range(5):
    user_email = ''.join(random.sample('1234567890abcdefghijklmnopqrstuvwxyz',8))+"@163.com"
    print(user_email)

'''
driver = webdriver.Chrome()
#driver = webdriver.Firefox()
driver.get("http://www.5itest.cn/register")
driver.maximize_window()
time.sleep(3)
print(EC.title_contains("注册"))
email_element = driver.find_element_by_id("register_email")
driver.save_screenshot("D:/www/SeleniumPython/ImoocSelenium/imooc.png")
code_element = driver.find_element_by_id("getcode_num")
print(code_element.location) #{"x":123,"y":345}
left = code_element.location['x']
top = code_element.location['y']
right = code_element.size['width']+left
height = code_element.size['height']+top
im = Image.open("D:/www/SeleniumPython/ImoocSelenium/imooc.png")
#resize the screenshot
img = im.crop((left,top,right,height))
img.save("D:/www/SeleniumPython/ImoocSelenium/imooc1.png")
driver.close()

'''
#verify if the element is found
#element = driver.find_elements_by_class_name("controls")
locator = (By.CLASS_NAME,"controls")
WebDriverWait(driver,1).until(EC.visibility_of_element_located(locator))

print(email_element.get_attribute("placeholder"))
email_element.send_keys("cyj@163.com")
print(email_element.get_attribute("value"))
driver.close()

driver.find_element_by_id("register_email").send_keys("himo2020@163.com")
user_name_element_node = driver.find_elements_by_class_name("controls")[1]
user_element = user_name_element_node.find_element_by_class_name("form-control")
user_element.send_keys("himo2020")
driver.find_element_by_name("password").send_keys("222222")
driver.find_element_by_xpath("//*[@id='captcha_code']").send_keys("111111")
'''


import requests
from bs4 import BeautifulSoup
from pylibz import log, deepcopy
from pylibz.func import base64_encode
from .config import get_javdb_host, get_local
from .func import get_cache, write_cache

def get_values(item):
    values = []
    contents = item.select("a")
    for i in range(len(contents)):
        values.append(contents[i].text)
    return values

def get_data(session: requests.Session, vid: str, vmsg: dict):
    vmsg = deepcopy(vmsg)
    url = "%s/v/%s" % (get_javdb_host(), vid)
    r = session.get(url)
    r = r.content.decode("utf-8")
    soup = BeautifulSoup(r, 'lxml')
    title = soup.select(".title")[0].select("strong")[0].text.strip()
    vmsg["title"] = title
    contents = soup.select(".panel-block")
    for i in range(len(contents) - 1):
        if len(contents[i].select("strong")) < 1:
            continue
        tag = contents[i].select("strong")[0].text
        tag = tag.strip().strip(":")
        if tag == '片商':
            t = get_values(contents[i])
            if isinstance(t, list):
                vmsg["writer"] += t
            else:
                vmsg["writer"].append(t)
        elif tag == '發行':
            t = get_values(contents[i])
            if isinstance(t, list):
                vmsg["writer"] += t
            else:
                vmsg["writer"].append(t)
        elif tag == '系列':
            tagline = get_values(contents[i])
            if isinstance(tagline, list):
                if len(tagline) > 0:
                    tagline = tagline[0]
                else:
                    tagline = ""
            vmsg["director"] = [tagline]
        elif tag == '日期':
            vmsg["release_date"] = contents[i].select('.value')[0].text
        elif tag == '類別':
            vmsg["genre"] = get_values(contents[i])
        elif tag == '演員':
            vmsg["actor"] = get_values(contents[i])
    backdrop = soup.select('.video-cover')[0].get("src")
    if backdrop.find("http") == -1:
        backdrop = "https:" + backdrop
    backdrop = "%s/javdb/img/%s" % (get_local(), base64_encode(backdrop))
    vmsg["extra"]["com.zanjo.javdb"]["backdrop"].append(backdrop)
    return vmsg

def search_javdb(query: str):
    query = query
    cache = get_cache("javdb", query)
    if cache is not None:
        return cache
    url = "%s/search?f=all&q=" % get_javdb_host()
    session = requests.session()
    session.headers = {
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.39"
    }
    r = session.get(url + query)
    r = r.content.decode("utf-8")
    try:
        soup = BeautifulSoup(r, "lxml")
    except Exception as e:
        log().warning(r)
        raise e
    contents = soup.select("#videos .grid-item.column")
    res = []
    for i in range(len(contents)):
        if i > 5:
            break
        vid = contents[i].select("a")[0].get('href').replace("/v/", "")
        title = contents[i].select(".uid")[0].text.strip()
        sub_title = contents[i].select(".video-title")[0].text.strip()
        original_available = contents[i].select(".meta")[0].text.strip()
        poster = contents[i].select(".item-image > img")[0].get("data-src")
        if poster.find("http") == -1:
            poster = "https:" + poster
        poster = "%s/javdb/img/%s" % (get_local(), base64_encode(poster))
        vmsg = {
            "title": "%s %s" % (title, sub_title),
            "tagline": sub_title,
            "original_available": original_available,
            "original_title": title,
            "summary": sub_title,
            "certificate": "",
            "genre": [
            ],
            "actor": [],
            "director": "",
            "writer": [],
            "extra": {
                "com.zanjo.javdb": {
                    "poster": [poster],
                    "backdrop": []
                }
            }
        }
        tt = title.lower().strip()
        if i < 1 or tt == query or tt in query or query in tt:
            try:
                vmsg = get_data(session, vid, vmsg)
            except:
                log().trace()
        res.append(vmsg)
    resp = {

        "success": True,
        "result": res
    }
    write_cache("javdb", query, resp)
    return resp


from prefect import task, Flow
from prefect.executors import LocalDaskExecutor
from prefect.storage import Docker
from prefect.run_configs import DockerRun

from research.example_pkg import things

@task#(log_stdout=True)
def run_thing1():
    if things.thing1():
        return True

@task#(log_stdout=True)
def run_thing2():
    if things.thing2():
        return True

if __name__ == "__main__":
    with Flow("test_flow", executor=LocalDaskExecutor(scheduler="processes")) as flow:
        if run_thing1():
            thing2_var = run_thing2()

    flow.storage = Docker(
        base_image="prefect_logger_repro",
        local_image=True,
        build_kwargs={
            "labels" : { "logging_job_name" : "test_flow" }
        },
        ignore_healthchecks=True,
        registry_url="artifactory.aq.tc/prefect/",
    )
    flow.run_config = DockerRun()
    #flow.run()
    flow.register(project_name="default")


#!/usr/bin/env python3

import sys
import argparse
import json

parser = argparse.ArgumentParser(description="Average piecewise-constant function")
parser.add_argument("--N",type=int,default=100,help="Number of points in reconstruction")
args = parser.parse_args()

N = args.N

def get_piecewise_fn_value(intervals,x):
    for rate,t1,t2 in intervals:
        if t1 <= x <= t2:
            return rate
    return None

def get_time_boundaries(intervals):
    (r0,t1_0,t2_0) = intervals[0]
    t1 = t1_0
    (rn,t1_n,t2_n) = intervals[-1]
    t2 = t2_n
    return (t1,t2)

def count_interval(N, total, intervals):
    t1,t2 = get_time_boundaries(intervals)
    for i in range(0,N):
        t = t1 + i*(t2-t1)/(N-1)
        total[i] += get_piecewise_fn_value(intervals,t)

with sys.stdin as file:
#       with open(filename,'r',encoding='utf8') as file:
    next(file,None)
    n = 0
    total = [0.0 for _ in range(0,N)]
    t1 = None
    t2 = None
    for line in file:
        n += 1
        intervals = json.loads(line)
        if t1 is None:
            t1,t2 = get_time_boundaries(intervals)
        count_interval(N, total, intervals)
    total2 = [ rate / n for rate in total]

    for i in range(0,N):
        t = t1 + i*(t2-t1)/(N-1)
        rate = total2[i]
        print(f"{t}\t{rate}")


import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import TimeSeriesSplit
from statsmodels.tsa.arima.model import ARIMA
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense

#Using oop
class TemperatureData:
    def __init__(self):
        self.data = None
        self.top_hot_years = TopHotYears()
    ##fulfilling the requirement of handling file and error
    def TemperatureFileLoad(self, fileName):
        try:
            self.data = pd.read_csv(fileName)
        except FileNotFoundError:
            print("File not found!")
        except pd.errors.EmptyDataError:
            print("Empty data file!")
        except pd.errors.ParserError:
            print("Error parsing the data file!")

    def filter_data(self, start_year=None, end_year=None):
        if start_year and end_year:
            self.data = self.data[(self.data[' Year'] >= start_year) & (self.data[' Year'] <= end_year)]

    def plotChart(self):
        if self.data is not None:
            plt.plot(self.data[' Year'], self.data['Temperature - (Celsius)'])
            plt.xlabel('Year')
            plt.ylabel('Temperature (Celsius)')
            plt.title('Temperature Change Over Time')
            plt.show()
        else:
            print("No data to plot!")

    def generate_statistical_summary(self):
        if self.data is not None:
            summary = self.data['Temperature - (Celsius)'].describe()
            print(summary)
        else:
            print("No data available!")

    def train_arima_model(self):
        if self.data is not None:
            tscv = TimeSeriesSplit(n_splits=3)
            for train_index, test_index in tscv.split(self.data):
                train_data = self.data.iloc[train_index]
                test_data = self.data.iloc[test_index]
                model = ARIMA(train_data['Temperature - (Celsius)'], order=(1, 0, 0))
                model_fit = model.fit()

                # Forecasting
                forecast = model_fit.predict(start=test_index[0], end=test_index[-1])
                print("Forecast:")
                print(forecast)
                plt.plot(test_data[' Year'], test_data['Temperature - (Celsius)'], label='Actual')
                plt.plot(test_data[' Year'], forecast, label='Forecast')
                plt.xlabel('Year')
                plt.ylabel('Temperature (Celsius)')
                plt.title('ARIMA Forecast')
                plt.legend()
                plt.show()

        else:
            print("No data available!")

    def train_lstm_model(self):
        if self.data is not None:
            # Prepare input and output data
            train_X = self.data['Temperature - (Celsius)'].shift(1).fillna(0).values.reshape(-1, 1)
            train_y = self.data['Temperature - (Celsius)'].values.reshape(-1, 1)

            # Create LSTM model
            model = Sequential()
            model.add(LSTM(128, activation='relu', input_shape=(1, 1)))
            model.add(Dense(1))
            model.compile(optimizer='adam', loss='mse')

            # Train the model
            model.fit(train_X, train_y, epochs=10)
            # Generate forecast
            forecast = model.predict(train_X[-10:])
            print("Forecast:")
            print(forecast)

        else:
            print("No data available!")

class TopHotYears:
    def __init__(self):
        self.top_temperatures = pd.DataFrame(columns=[' Year', 'Temperature - (Celsius)'])
    def find_top_hot_years(self, data, n=10):
        if data is not None:
            sorted_df = data.sort_values('Temperature - (Celsius)', ascending=False)
            top_n_years = sorted_df[' Year'].unique()[:n]
            for year in top_n_years:
                max_temperature_year = sorted_df[sorted_df[' Year'] == year].iloc[0]
                self.top_temperatures = pd.concat([self.top_temperatures, max_temperature_year.to_frame().T])
            return self.top_temperatures[[' Year', 'Temperature - (Celsius)']]
        else:
            print("No data available!")

temp_data = TemperatureData()
temp_data.TemperatureFileLoad('Tempreture_1901_2016_Pakistan.csv')
temp_data.filter_data(1910, 2010)
temp_data.plotChart()
temp_data.generate_statistical_summary()
temp_data.train_arima_model()
temp_data.train_lstm_model()
# Find the top 10 hot years
top_hot_years = temp_data.top_hot_years.find_top_hot_years(temp_data.data, 10)
print(top_hot_years)


# coding: utf-8
from keras.preprocessing.text import Tokenizer

samples = ['the cat sat on the mat.', 'the dog ate my homework.']

# 出現頻度がもっとも高い1000個の単語だけを処理するトークナイザー
tokenizer = Tokenizer(num_words=1000)

# 単語のインデックスを構築
tokenizer.fit_on_texts(samples)

# 文字列を整数のインデックスのリストに変換
#sequences = tokenizer.texts_to_sequences(samples)

#print(sequences)
# [[1, 2, 3, 4, 1, 5], [1, 6, 7, 8, 9]]

#one_hot_results = tokenizer.texts_to_matrix(samples, mode='binary')

#print(one_hot_results)
# [[0. 1. 1. ... 0. 0. 0.]
#  [0. 1. 0. ... 0. 0. 0.]]

# 計算された単語のインデックスを復元
word_index = tokenizer.word_index

print(word_index)
# {'the': 1, 'cat': 2, 'sat': 3, 'on': 4, 'mat': 5, 'dog': 6, 'ate': 7, 'my': 8, 'homework': 9}


from marshmallow import Schema, fields, EXCLUDE

class BookSchema(Schema):
    title = fields.Str()
    author = fields.Str()
    description = fields.Str()

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

incoming_book_data = {
    "title": "Clean Code",
    "author": "Bob Martin",
}

book_schema = BookSchema()
book = book_schema.load(incoming_book_data)
book_obj = Book(**book)

print(book_obj)


from flask import Blueprint, render_template, request

from app import db
from products.models import TV, Fridge

products = Blueprint('products', __name__, template_folder='templates', static_folder='static')

def sort(data):
    # Сортируем по параметру выбранном пользователем
    if request.form["sort"] == "click_amount":
        data.sort(key=lambda product: product.click_count, reverse=True)
    else:
        data.sort(key=lambda product: product.name)

@products.route("/tvs", methods=['GET', 'POST'])
def tvs_page():
    all_tvs = TV.query.all()

    # Проверяем есть ли запрос на сортировку с формы
    if "sort" in request.form:
        sort(all_tvs)
    else:
        # Если с формы запрос не отправлен, то сортируем по имени
        all_tvs.sort(key=lambda tv: tv.name)

    return render_template("products.html", products=all_tvs, type="tvs")

@products.route("/fridges", methods=['GET', 'POST'])
def fridges_page():
    all_fridges = Fridge.query.all()

    # Проверяем есть ли запрос на сортировку с формы
    if "sort" in request.form:
        sort(all_fridges)
    else:
        # Если с формы запрос не отправлен, то сортируем по имени
        all_fridges.sort(key=lambda fridges: fridges.name)

    return render_template("products.html", products=all_fridges, type="fridges")

@products.route('/click', methods=['POST'])
def product_click():
    """
    Функция вызываемая, когда AJAX делает запрос, сигнализируя, что был сделан клик
    """
    data = eval(request.data)

    if data['type'] == "fridges":
        product = Fridge.query.filter(Fridge.name == data['name']).first()
        all_products = Fridge.query.all()
    else:
        product = TV.query.filter(TV.name == data['name']).first()
        all_products = TV.query.all()

    product.click_count += 1
    db.session.commit()

    return render_template("products.html", products=all_products, type=data['type'])