# -*- coding: utf-8 -*-

import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt

from astropy.io import fits
from astropy.constants import c
import astropy.wcs as fitswcs
import astropy.units as u
from astropy.modeling import models
from astropy.table import Table

from box import Box
from specutils import Spectrum1D

class FadoLoad:
    """
    This class is meant to load the necessary files and keep them in memory
    to be used in subsequent classes.

    It should also read some important information

    Attributes
    ----------
    EL_file : str
            File name containing the emission line results
    ST_file : str
            File name containing the statistical results
    ONED_file : str
            File name containing the 1D spectra
    DE_file : str
            File name containing the differential evolution results
    redshift : float
            The object redshift
    rederror : float
            The error in the object redshift
    fado_ver : str
            Fado version
    ext_law : str
            Extinction law used

    """
    def __init__(self, name):

        self.EL_file = name + "_EL.fits"
        self.ST_file = name + "_ST.fits"
        self.ONED_file = name + "_1D.fits"
        self.DE_file = name + "_DE.fits"

        self.redshift = self.ONED.header["REDSHIFT"]
        self.rederror = self.ONED.header["REDERROR"]
        self.fado_ver = self.ONED.header["FADO_VER"]
        self.ext_law = self.ONED.header["R_LAWOPT"]

        self.wave_unit = u.AA
        self.norm = self.ONED.header["GALSNORM"]
        self.flux_unit = self.norm * 10**self.ONED.header["FLUXUNIT"] * u.Unit("erg / (s cm**2 Angstrom)")

    @property
    def EL(self):
        """
        Property that returns the header and data of EL line results.

        Returns
        -------
        self.EL.header
        self.EL.data
        """
        with fits.open(self.EL_file, lazy_load_hdus=False) as hdu:
            return Box({"header": hdu[0].header, "data": hdu[0].data})

    @property
    def ST(self):
        """
        Property that returns the header and data of ST results.

         Returns
         -------
         self.ST.header
         self.ST.data
         """
        with fits.open(self.ST_file, lazy_load_hdus=False) as hdu:
            return Box({"header": hdu[0].header, "data": hdu[0].data})

    @property
    def ONED(self):
        """
         Property that returns the header and data of 1D results.

          Returns
          -------
          self.ONED.header
          self.ONED.data
          """

        with fits.open(self.ONED_file, lazy_load_hdus=False) as hdu:
            return Box({"header": hdu[0].header, "data": hdu[0].data})

    @property
    def DE(self):
        """
        Property that returns the header and data of DE results.

        Returns
        -------
        self.DE.header
        self.DE.data
        """
        with fits.open(self.DE_file, lazy_load_hdus=False) as hdu:
            return Box({"header": hdu[0].header, "data": hdu[0].data})

class OneD:
    """
    Class to manage the results in the ONED File

    Attributes
    ----------
    fado_load : FadoLoad object
    header : dict
            header of the 1D file
    data : array-like
            data of the 1D file
    max_rows : int
            maximum number of rows
    """
    def __init__(self, fado_load=FadoLoad):
        self.fado_load = fado_load
        self.header = fado_load.ONED.header
        self.data = fado_load.ONED.data
        self.max_rows = self.data.shape[0]

    @property
    def wcs(self):
        """

        Returns
        -------
        WCS
        """
        wcs = fitswcs.WCS(header={'CDELT1': self.header["CDELT1"],
                        'CRVAL1': self.header["CRVAL1"],
                        'CUNIT1': 'Angstrom',
                        'CTYPE1': 'WAVE',
                        'CRPIX1': self.header["CRPIX1"]})
        return wcs

    def spectrum(self, row=1, scale=True):
        """
        Creates a specutils.Spectrum1D from FADO ONED files.

        The spectrum can be specified as a row or as a name.
        Allowed names are the following

        1: 'Observed'         spectrum de-redshifted and rebinned'
        2: 'Error'
        3: 'Mask'
        4: 'Best'
        5: 'Average'            of individual solutions
        6: 'Median'
        7: 'Stdev'
        8: 'Stellar'            using best fit'
        9: 'Nebular'            using best fit'
        10: 'AGN'               using best fit'
        11: 'M/L'
        12: 'LSF'  Line spread function

        The best-fits for individual solution can only be specified as a number 13-XX
        see self.max_rows

        Parameters
        ----------
        row: str, int
            Allowed names: 'observed', 'error', 'mask', 'best', 'average', 'median', 'stdev', 'stellar', 'nebular'
                'agn', 'm/l', 'lsf'
            The row number where the spectra is extracted, default=1 (0 in python notation)

        scale: bool, whether to scale the spectra or not, default True

        Returns
        -------
        a specutils.Spectrum1D object
        """
        row_names = {'observed': 1,  'error': 2,  'mask': 3,
                     'best fit': 4, 'average': 5,  'median': 6,
                     'stdev': 7,  'stellar': 8,  'nebular': 9,
                     'm/l': 10,   "lsf": 12}
        if isinstance(row, str):
            try:
                row = row_names[row.lower()] - 1
            except (KeyError, AttributeError) as e:
                print("name not found", e)
        else:
            row = row - 1

        if scale is False:
            scale = 1 * u.dimensionless_unscaled
        else:
            scale = self.fado_load.flux_unit

        flux = self.data[row] * scale
        sp = Spectrum1D(flux=flux, wcs=self.wcs)
        return sp

    def to_mpdaf(self, row=1, scale=True):
        """
        Creates a mpdaf.obj.Spectrum object from FADO 1D files.

        The spectrum can be specified as a row or as a name.
        Allowed names are the following

        1: 'Observed'         spectrum de-redshifted and rebinned'
        2: 'Error'
        3: 'Mask'
        4: 'Best'
        5: 'Average'            of individual solutions
        6: 'Median'
        7: 'Stdev'
        8: 'Stellar'            using best fit'
        9: 'Nebular'            using best fit'
        10: 'AGN'               using best fit'
        11: 'M/L'
        12: 'LSF'  Line spread function

        The best-fits for individual solution can only be specified as a number 13-XX
        see self.max_rows

        Parameters
        ----------
        row: str, int
            Allowed names: 'observed', 'error', 'mask', 'best', 'average', 'median', 'stdev', 'stellar', 'nebular'
                'agn', 'm/l', 'lsf'
            The row number where the spectra is extracted, default=1 (0 in python notation)

        scale: bool, whether to scale the spectra or not, default True

        Returns
        -------
        a mpdaf.obj.Spectrum object
        """
        try:
            from mpdaf.obj import Spectrum, WaveCoord
        except ImportError as e:
            print(e, "MPDAF not installed, cannot continue")
            raise
        sp = self.spectrum(row, scale)
        wave_coord = WaveCoord(cdelt=self.header["CDELT1"],
                               crval=self.header["CRVAL1"],
                               cunit=self.fado_load.wave_unit)

        sp_mpdaf = Spectrum(wave=wave_coord, data=sp.flux)

        return sp_mpdaf

    def to_table(self, row=1, scale=True):
        """
        Creates an astropy.table.Table object from FADO 1D files.

        The spectrum can be specified as a row or as a name.
        Allowed names are the following

        1: 'Observed'         spectrum de-redshifted and rebinned'
        2: 'Error'
        3: 'Mask'
        4: 'Best'
        5: 'Average'            of individual solutions
        6: 'Median'
        7: 'Stdev'
        8: 'Stellar'            using best fit'
        9: 'Nebular'            using best fit'
        10: 'AGN'               using best fit'
        11: 'M/L'
        12: 'LSF'  Line spread function

        The best-fits for individual solution can only be specified as a number 13-XX
        see self.max_rows

        Parameters
        ----------
        row: str, int
            Allowed names: 'observed', 'error', 'mask', 'best', 'average', 'median', 'stdev', 'stellar', 'nebular'
                'agn', 'm/l', 'lsf'
            The row number where the spectra is extracted, default=1 (0 in python notation)

        scale: bool, whether to scale the spectra or not, default True

        Returns
        -------
        an astropy.table.Table object
        """
        sp = self.spectrum(row, scale)
        table = Table(data=(sp.wavelength, sp.flux),
                      names=("wavelength", "flux" ))

        return table

class EmLines:
    """
    Class to manage the results in the 1D files

    Attributes
    ----------
    scale : u.Quantity
        the scaling applied to the emission line flux
    header : dict
        the header of the 1D file
    data : array-like
        the data of the 1D file
    names : dict
        the emission line names
    waves : dict
        the center of the emission lines
    info : dict
        the pointers where to find the results of the emission line fitting in the data
    """

    def __init__(self, fado_load=FadoLoad):
        self.fado_load = fado_load
        self.scale = self.fado_load.flux_unit
        self.header = fado_load.EL.header
        self.data = fado_load.EL.data
        self.names = self.line_dicts["names"]
        self.waves = self.line_dicts["waves"]
        self.info = self.line_dicts["info"]

    @property
    def line_dicts(self):
        """ update the attributes of the lines fitted in the spectra
        self.line_names is a list with the names
        self.lines_waves = a dictionary with the rest-frame wavelengths
        self.line_info  = a dictionary with the pointers to the results
        """
        keys = list(self.header.keys())
        el_keys = [k for k in keys if "EL___" in k]
        el_names = []
        el_waves = {}
        el_info = {}
        for k in el_keys:
            [name, wave, rows] = self.header[k].split()

            name = name + "_" +wave.split(".")[0]
            el_names.append(name)
            el_waves.update({name: float(wave)*u.AA})
            el_info.update({name: rows})

        return {'names': el_names, 'waves': el_waves, "info": el_info}

    @property
    def param(self):
        """
        N_PARAMS: 21 | Number of parameters
        TOTLINES: 1071 | Total  number   of   parameters    used
        ISNEBCON: 1 | Nebular        continuum
        ISNEBCPL: 0 | Planned        for future versions
        ISLEAKON: 0 | Planned             for future versions
        SELF_CON: 2 | Self - consistency
        CONVERGE: 1 | Convergence
        FLAG_BPT: 0 | BPT flag for [NII] / Ha and [OIII] / Hb(0: Pure SF / 1: SF / 2:Composite / 3: LINER / 4:Seyfert)
        ELAPSEDT: 106.40000000000 | Elapsed        time in seconds
        LAMBDA_0: 4020.0000000000 | Normalization        wavelength
        GALSNORM: 47.580000000000 | Flux        at        the     normalization wavelength
        FLUXUNIT: 0.0000000000000 | Flux        units in log10
        LOGBN2HA: -1.9400000000000 | log[NII] / Ha
        ERRBN2HA: 0.14300000000000E-01 | errorlog[NII] / Ha
        LOGBO3HB: -1.9400000000000 | log[OIII] / Hb
        ERRBO3HB: 0.14300000000000E-01 | error        log[OIII] / Hb
        TELECTRO: 15150.000000000 | Electron       temperature
        DELECTRO: 171.40000000000 | Electron density
        GEXTINCT: 0.71940000000000 | Stellar   extinction
        GEXTBDEV: 0.45930000000000E-07 | Error associated to stellar extinction
        GNEBULAR: 1.2040000000000 | Nebularextinction
        GNEBBDEV: 0.0000000000000 | Error associated to nebular extinction

        Returns
        -------
        self.lambda_0 : Normalization wavelength
        self.flat_bpt : The BTP flag = 0 for SF, =2 Composite, =3 Liner, =4 Seyfert
        self.telectro : The electron temperature
        self.delectro : The electron density
        self.gextinct : The stellar extinction
        self.gnebular : The nebular extinction

        """
        return Box({"lambda_0" : self.header["LAMBDA_0"],
                    "flag_bpt": self.header["FLAG_BPT"],
                    "telectro": self.header["TELECTRO"],
                    "delectro": self.header["DELECTRO"],
                    "gextinct": self.header["GEXTINCT"],
                    "gnebular": self.header["GNEBULAR"]
                    })

    def results(self, line_name, mode="best"):
        """
        Returns a dictionary with the results of the fit for a emission line.

        Parameters
        ----------
        line_name : str
            name of the emission line see self.names for a list of available names
        mode : str,
            "best" (default), "mean" or "median"
        """
        index = 0
        if mode == "mean":
            index = index + 7
        if mode == "median":
            index = index + 14

        rows = self.info
        window = rows[line_name].split('--')
        xmin = int(window[0])-1
        xmax = int(window[1])
        values = self.data[:, xmin:xmax][0]
        results = {'lambda': values[index] * u.AA,
                   'amplitude': values[index + 1] * self.scale,
                   'sigma': values[index + 2] * u.AA,
                   'vel': values[index + 3] * u.km / u.s,
                   'shift': values[index + 4] * u.AA,
                   'flux': values[index + 5] * self.scale,
                   'ew': values[index + 6] * u.AA}

        return results

    def errors(self, line_name, mode="best"):
        """
        Returns a dictionary with the errors  of the fit for a emission line.

        Parameters
        ----------
        line_name: str,
            name of the emission line see self.names for a list of available names
        mode: str,
            "best" (default), "mean" or "median"

        Returns
        -------
        dict
        """
        index = 0
        if mode == "mean":
            index = index + 7
        if mode == "median":
            index = index + 14

        rows = self.info
        window = rows[line_name].split('--')
        xmin = int(window[0]) - 1
        xmax = int(window[1])
        values = self.data[:, xmin:xmax][1]
        errors = {'lambda': values[index] * u.AA,
                  'amplitude': values[index + 1] * self.scale,
                  'sigma': values[index + 2] * u.AA,
                  'vel': values[index + 3] * u.km / u.s,
                  'shift': values[index + 4] * u.AA,
                  'flux': values[index + 5] * self.scale,
                  'ew': values[index + 6] * u.AA}

        return errors

    def line_spectra(self, line_name, mode="best"):
        """
        Create a specutils.Spectrum1D for the line

        Parameters
        ----------
        line_name : str
                name of the emission line see self.names for a list of available names

        mode : str
            'best', 'mean' or 'median' of the solutions

        Returns
        -------
        specutils.Spectrum1D
        """
        results = self.results(line_name, mode=mode)
        lambda_r = results["lambda"]
        vel = results["vel"]
        sigma = results["sigma"]
        amplitude = results["amplitude"]

        center = (vel / c.to('km/s')) * lambda_r + lambda_r
        spectrum = OneD(self.fado_load)
        wcs = spectrum.wcs

        length = self.fado_load.ONED.header["NAXIS1"]  # size of the spectrum in pixels
        sp = Spectrum1D(flux=np.zeros(length) * self.scale,
                        wcs=wcs)

        model = models.Gaussian1D(amplitude=amplitude,
                                  mean=center,
                                  stddev=sigma)
        sp = sp + Spectrum1D(spectral_axis=sp.spectral_axis, flux=model(sp.wavelength))
        return sp

    def to_table(self, mode="best"):
        """
        Create an astropy table with the results of the emission line fitting

        Parameters
        ----------
        mode: str
            best (default), mean or median

        Returns
        -------
        astropy.table.Table
        """
        column_names = ("line_name",
                        "lambda",  "amplitude", "sigma", "vel", "shift", "flux", "ew",
                        "lambda_err",  "amplitude_err", "sigma_err", "vel_err", "shift_err", "flux_err", "ew_err")
        dtypes = ['S11'] + ['f8']*14
        table = Table(names=column_names, dtype=dtypes)

        for name in self.names:
            results = self.results(name, mode=mode)
            errors = self.errors(name, mode=mode)
            row = [name] + \
                  [results[key] for key in results.keys()] + \
                  [errors[key] for key in errors.keys()]
            table.add_row(row)

        return table

class Stats:
    """
    Class to manage the results in the statistical file
    """
    def __init__(self, fado_load=FadoLoad):
        self.fado_load = fado_load
        self.header = fado_load.ST.header
        self.data = fado_load.ST.data
        self.scale = self.fado_load.flux_unit

    @property
    def param(self):
        """
        LAMBDA_0=            5.710E+03 / Norm. wavelength
        GALSNORM=            1.175E+01 / Norm. flux
        FLUXUNIT=           -2.000E+01 / Flux units in log10
        L_DISMPC=            1.554E+03 / Luminosity dist
        TELECTRO=            1.000E+04
        DELECTRO=            5.655E+02
        GEXTINCT=            1.439E+00
        GEXTBAVE=            1.439E+00
        GEXTBDEV=            5.123E-07
        GNEBULAR=            4.809E-01
        GNEBBAVE=            4.809E-01
        GNEBBDEV=            1.724E-08
        V0SYSGAL=            5.349E+01
        V0SYSAVE=            5.330E+01
        V0SYSDEV=            7.105E-15
        VTSYSGAL=            1.053E+05
        VTSYSAVE=            1.053E+05
        VTSYSDEV=            1.503E+02
        VDSYSGAL=            1.798E+02
        VDSYSAVE=            1.842E+02
        VDSYSDEV=            2.842E-14
        V0NEBGAL=            5.349E+01
        V0NEBAVE=            5.330E+01
        V0NEBDEV=            7.105E-15
        VTNEBGAL=            1.053E+05
        VTNEBAVE=            1.053E+05
        VTNEBDEV=            1.503E+02
        VDNEBGAL=            1.798E+02
        VDNEBAVE=            1.842E+02
        VDNEBDEV=            2.842E-14

        Returns
        -------

        """
        keys = ["LAMBDA_0",
                "GALSNORM",
                "FLUXUNIT",
                "L_DISMPC",
                "TELECTRO",
                "DELECTRO",
                "GEXTINCT",
                "GEXTBAVE",
                "GEXTBDEV",
                "GNEBULAR",
                "GNEBBAVE",
                "GNEBBDEV",
                "V0SYSGAL",
                "V0SYSAVE",
                "V0SYSDEV",
                "VTSYSGAL",
                "VTSYSAVE",
                "VTSYSDEV",
                "VDSYSGAL",
                "VDSYSAVE",
                "VDSYSDEV",
                "V0NEBGAL",
                "V0NEBAVE",
                "V0NEBDEV",
                "VTNEBGAL",
                "VTNEBAVE",
                "VTNEBDEV",
                "VDNEBGAL",
                "VDNEBAVE",
                "VDNEBDEV"]

        params = {k: self.header[k] for k in keys}
        return Box(params)

class FadoDE:
    """
    Class to manage the results in the DE files
    """
    pass

class FADO:
    """
    Legacy:
    This class is supposed to load and extract parameters from FADO files

    """

    EL_file = None
    ST_file = None
    ONED_file = None
    DE_file = None

    def __init__(self, EL_file, ST_file, ONED_file, DE_file):

        self.EL_file = EL_file
        self.ST_file = ST_file
        self.ONED_file = ONED_file
        self.DE_file = DE_file

    # open_the_files
        self.hdu_EL = fits.open(self.EL_file)
        self.hdu_ST = fits.open(self.ST_file)
        self.hdu_ONED = fits.open(self.ONED_file)
        self.hdu_DE = fits.open(self.DE_file)

    def files(self):
        """
        just print the files being used
        """
        print(self.EL_file)
        print(self.ST_file)
        print(self.ONED_file)
        print(self.DE_file)

    def ELnames(self):
        """
        create dict of emission lines and their wavelengths
        """

        head = self.hdu_EL[0].header
        keys = list(head.keys())
        el_keys = [k for k in keys if "EL___" in k ]

        el_waves = {}
        for k in el_keys:
            [name, wave, rows] = head[k].split()
            if "[" in name:
                name = name + wave.split(".")[0]

            el_waves.update( {name: float(wave)})

        return el_waves

    def ELrows(self):
        """
        create dict of emission lines and their windows for data
        """

        head = self.hdu_EL[0].header
        keys = list(head.keys())
        el_keys = [k for k in keys if "EL___" in k ]

        el_rows = {}
        for k in el_keys:
            [name, wave, rows] = head[k].split()
            if "[" in name:
                name = name + wave.split(".")[0]

            el_rows.update( {name: rows} )

        return el_rows

    def waves(self):
        """
        Return the wavelength grid
        """
        head = self.hdu_ONED[0].header
        NAXIS1 = head["NAXIS1"]
        CRVAL1 = head["CRVAL1"]
        CRPIX1 = head["CRPIX1"] - 1 # because python
        CRDELT1 = head["CDELT1"]
        waves = CRVAL1 + CRDELT1*np.arange(CRPIX1, NAXIS1, 1)

        return waves

    def print_rows(self):
        """
        print what the rows of 1D files contain
        """
        head = self.hdu_ONED[0].header
        keys = list(head.keys())
        for k in keys:
            if "ROW" in k:
                print(k + '= ' ,repr(head[k]))

        print()
        print("Maximum row number=", head["NAXIS2"])

    def get_spectra(self, row):
        """
        return a numpy array with the spectra of that row
        see print_rows for description
        """
        data = self.hdu_ONED[0].data
        if row<1 and row>data.shape[1]:
            raise ValueError("row number not valid")

        spec = data[row-1]

        return spec

    def plot_spec(self, row, wmin=None, wmax=None, **kwargs):
        """
        plot a spectra from 1D files between wmin and wmax (default all)
        **kwargs are passed to matplotlib.pyplot
        """
        waves = self.waves()
        spec = self.get_spectra(row)
        if wmin is None:
            wmin = waves[0]
        if wmax is None:
            wmax = waves[-1]

        spec = spec[(waves>=wmin) & (waves<=wmax)]
        waves = waves[(waves>=wmin) & (waves<=wmax)]
        plt.plot(waves, spec, **kwargs)

    def plot_obs_spectrum(self, row=None, wmin=None, wmax=None, **kwargs):
        """
        plot the observed spectrum between wmin and wmax (default all)
        row number is optional in case observed spectrum is in another row
        check using print_rows
        """
        if row is None:
            row = 1

        self.plot_spec(row, wmin, wmax, **kwargs)

    def plot_model_spectrum(self, row=None, wmin=None, wmax=None, **kwargs):
        """
        plot the best fit FADO spec
        """
        if row is None:
            row = 4

        self.plot_spec(row, wmin, wmax, **kwargs)

    def plot_residual(self, wmin=None, wmax=None, **kwargs):
        """
        plot the residuals: observed - model
        """
        waves = self.waves()
        obs = self.get_spectra(1)
        model = self.get_spectra(4)
        if wmin is None:
            wmin = waves[0]
        if wmax is None:
            wmax = waves[-1]

        obs = obs[(waves>=wmin) & (waves<=wmax)]
        model = model[(waves>=wmin) & (waves<=wmax)]
        waves = waves[(waves>=wmin) & (waves<=wmax)]
        plt.plot(waves, obs - model, **kwargs)

    def plot_line(self, linename, window=None, **kwargs):
        """
        plot the region around a emission line
        for names see ELnames
        window is optional defaul 100A
        """
        if window is None:
            window = 100

        ELdict = self.ELnames()
        center = ELdict[linename]
        wmin = center - 0.5*window
        wmax = center + 0.5*window

        self.plot_obs_spectrum(row=1, wmin=wmin, wmax=wmax, **kwargs)

    def plot_line_residual(self, linename, window=None, **kwargs):
        """
        as above but after subtracting the model spectrum
        """
        if window is None:
            window = 100

        ELdict = self.ELnames()
        center = ELdict[linename]
        wmin = center - 0.5*window
        wmax = center + 0.5*window

        self.plot_residual(wmin=wmin, wmax=wmax, **kwargs)

    def get_results(self, linename):
        """
        returns a dictionary with the results of the fit for a emission line
        see ELnames for names
        """
        data = self.hdu_EL[0].data
        rows = self.ELrows()
        window = rows[linename].split('--')
        xmin = int(window[0])-1
        xmax = int(window[1])-1

        values = data[:, xmin:xmax][0]

        results = {'Lambda':values[0],
                   'Amplitude':values[1],
                   'Sigma':values[2],
                   'Vel':values[3],
                   'shift':values[4],
                   'Flux':values[5],
                   'EW':values[6]}

        return results

    def get_errors(self, linename):
        """
        returns a dictionary with the results of the fit for a emission line
        see ELnames for names
        """
        data = self.hdu_EL[0].data
        rows = self.ELrows()
        window = rows[linename].split('--')
        xmin = int(window[0])-1
        xmax = int(window[1])-1

        values = data[:,xmin:xmax][1]

        errors = {'Lambda':values[0],
                   'Amplitude':values[1],
                   'Sigma':values[2],
                   'Vel':values[3],
                   'shift':values[4],
                   'Flux':values[5],
                   'EW':values[6]}

        return errors

    def plot_fit_to_line(self, linename, window=None, **kwargs):
        """
        plot the fits of to a line
        works better with plot_line_residuals
        """
        results = self.get_results(linename)
        if window is None:
            window = 100

        ELdict = self.ELnames()
        center = ELdict[linename]
        wmin = center - 0.5*window
        wmax = center + 0.5*window

        lambda_r = results["Lambda"]
        vel = results["Vel"]
        mu = (vel/c.to('km/s').value) * lambda_r + lambda_r
        sigma = results["Sigma"]
        flux = results["Flux"]
        x = self.waves()
        x = x[(x>wmin) & (x<wmax)]
        y = flux*norm.pdf(x, mu, sigma)

        plt.plot(x, y, **kwargs)

{'Spectrum': 'mpdaf.obj.Spectrum', 'WaveCoord': 'mpdaf.obj.WaveCoord'}


from utils.parse import Parser
from aoc.intcode import Machine
from copy import deepcopy
from enum import Enum

class Pixel(Enum):
    SPACE = 0
    SCAFFOLD = 1

class Camera:
    def __init__(self, program):
        self.__cpu = Machine(program, lambda: None, self.__get_pixel)
        self.__image = {}
        self.__position = (0, 0)

        self.__char_mapping = {
            '.': Pixel.SPACE,
            '#': Pixel.SCAFFOLD,
            '^': Pixel.SCAFFOLD,
            '>': Pixel.SCAFFOLD,
            'v': Pixel.SCAFFOLD,
            '<': Pixel.SCAFFOLD
        }

    @property
    def image(self):
        return self.__image

    def run(self):
        self.__cpu.run()

    def __get_pixel(self, ascii):
        c = chr(ascii)
        x, y = self.__position

        mapping = self.__char_mapping.get(c, None)
        if mapping is None:
            self.__position = 0, y + 1
            return

        self.__image[self.__position] = mapping
        self.__position = x + 1, y

parser = Parser("Day 17: Set and Forget - Part 1")
parser.parse()
with parser.input as input:
    line = input.readline()
    program = [int(el) for el in line.split(',')]

camera = Camera(deepcopy(program))
camera.run()

image = camera.image

min_x = min(x for x, _ in image.keys())
min_y = min(y for _, y in image.keys())
max_x = max(x for x, _ in image.keys())
max_y = max(y for _, y in image.keys())

directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]

scaffold_scan = ((x, y) for y in range(min_y, max_y + 1)
                 for x in range(min_x, max_x + 1) if image[(x, y)] is Pixel.SCAFFOLD)
total_alignment = 0
for x, y in scaffold_scan:
    adjacent = ((x + d_x, y + d_y) for d_x, d_y in directions)
    adjacent_scaffolds = sum(
        1 for p in adjacent if image.get(p, None) is Pixel.SCAFFOLD)
    if adjacent_scaffolds > 2:
        total_alignment += x * y

print(total_alignment)


import uuid

import pytest
from fakeredis.aioredis import FakeRedis
from fastapi import FastAPI
from httpx import AsyncClient
from starlette import status

@pytest.mark.anyio
async def test_setting_value(
    fastapi_app: FastAPI,
    fake_redis: FakeRedis,
    client: AsyncClient,
) -> None:
    """
    Tests that you can set value in redis.

    :param fastapi_app: current application fixture.
    :param fake_redis: fake redis instance.
    :param client: client fixture.
    """
    url = fastapi_app.url_path_for("handle_http_post")

    test_key = uuid.uuid4().hex
    test_val = uuid.uuid4().hex
    query = """
    mutation ($key: String!, $val: String!) {
        setRedisValue(data: { key: $key, value: $val }) {
            key
            value
        }
    }
    """
    response = await client.post(
        url,
        json={
            "query": query,
            "variables": {"key": test_key, "val": test_val},
        },
    )

    assert response.status_code == status.HTTP_200_OK
    actual_value = await fake_redis.get(test_key)
    assert actual_value == test_val

@pytest.mark.anyio
async def test_getting_value(
    fastapi_app: FastAPI,
    fake_redis: FakeRedis,
    client: AsyncClient,
) -> None:
    """
    Tests that you can get value from redis by key.

    :param fastapi_app: current application fixture.
    :param fake_redis: fake redis instance.
    :param client: client fixture.
    """
    test_key = uuid.uuid4().hex
    test_val = uuid.uuid4().hex
    await fake_redis.set(test_key, test_val)
    url = fastapi_app.url_path_for("handle_http_post")
    response = await client.post(
        url,
        json={
            "query": "query($key:String!){redis:getRedisValue(key:$key){key value}}",
            "variables": {
                "key": test_key,
            },
        },
    )

    assert response.status_code == status.HTTP_200_OK
    assert response.json()["data"]["redis"]["key"] == test_key
    assert response.json()["data"]["redis"]["value"] == test_val


import json
import os
import re
import base64

from pathlib import Path

scriptLocation = os.path.dirname(os.path.realpath(__file__))
TUTORIAL_JSON_FILES = [Path(scriptLocation) / 'archery' / 'archery.vue.json']

indexTemplate = open(Path(scriptLocation) / 'assets' / 'tutorial.html').read()
bulmaCss = open(Path(scriptLocation) / 'assets' / 'bulma.min.css').read()
vueScript = open(Path(scriptLocation) / 'assets' / 'vue.min.js').read()
vueRouterScipt = open(
    Path(scriptLocation) / 'assets' / 'vue-router.min.js').read()
tutorialApp = open(Path(scriptLocation) / 'assets' / 'app.js').read()

checkboxRegex = re.compile(r'\[(.*?)\]\(checkbox\)')
markdownLinkRegex = re.compile(r'\[(.*?)\]\((.*?)\)')

for jsonFile in TUTORIAL_JSON_FILES:

    with open(jsonFile) as fo:
        content = fo.read()
        tutorialData = json.loads(content)
        json_path = Path(os.path.dirname(jsonFile))

        for index, slide in enumerate(tutorialData['slides']):
            if slide.get('image', None):
                with open(json_path / slide['image']['file'], 'rb') as fb:
                    data = base64.b64encode(fb.read())
                    tutorialData['slides'][index]['image']['src'] = \
                        "data:image/gif;base64," + str(data, 'utf-8')

            tutorialData['slides'][index]['text'] = ('
'
                                                     + '

'.join(
                        slide['text'].split('\n')
                    )
                                                     + '
'
                                                     )
            if slide.get('foot', None):
                tutorialData['slides'][index]['foot'] = ('
'
                                                         + '

'.join(
                            slide['foot'].split('\n')
                        )
                                                         + '
'
                                                         )

        finalFile = indexTemplate.format(tutorial_title=tutorialData['name'],
                                         bulma=bulmaCss,
                                         tutorial_json=tutorialData,
                                         vuejs=vueScript,
                                         vuerouter=vueRouterScipt,
                                         appjs=tutorialApp)
        pageHtmlFilename = os.path.join(os.path.dirname(jsonFile),
                                        '%s.html' % (tutorialData[
                                                         'name'].lower()))
        with open(pageHtmlFilename, 'w') as fo:
            fo.write(finalFile)


# coding=utf-8
#

import unittest2
from risk_report.asset import getAssetType, getAverageRatingScore, isInvestmentGrade
from risk_report.main import ratingsApplicable
from risk_report.data import getIdnType, getPortfolioPositions, getBlpData
from itertools import filterfalse
from functools import partial
from utils.iter import firstOf
from toolz.functoolz import compose
from os.path import join

class TestAsset(unittest2.TestCase):

	def __init__(self, *args, **kwargs):
		super(TestAsset, self).__init__(*args, **kwargs)

	def testDIFAssetType(self):
		positions = list(getPortfolioPositions('19437', '20200429', 'test'))
		blpData = getBlpData('20200429', 'test')

		# USD cash on hand position
		isUSDCash = lambda x: \
			x['InvestID'] == 'USD' and int(x['Quantity']) == 8183675
		self.assertEqual( ('Cash', )
						, getAssetType(blpData, firstOf(isUSDCash, positions)))

		# Cash payable position
		isHKDCashPayable = lambda x: \
			x['InvestID'] == 'HKD' and int(x['Quantity']) == -1804761
		self.assertEqual( ('Cash', )
						, getAssetType(blpData, firstOf(isHKDCashPayable, positions)))

		# Equity position
		isEquityPosition = lambda x: x['InvestID'] == '1299 HK'
		self.assertEqual( ('Equity', 'Listed Equities')
						, getAssetType(blpData, firstOf(isEquityPosition, positions)))

		# The bond: T V2.875 PERP B
		isBondPosition = lambda x: x['InvestID'] == 'XS2114413565'
		self.assertEqual( ('Fixed Income', 'Corporate')
						, getAssetType(blpData, firstOf(isBondPosition, positions)))

		# The bond: POSABK V4.5 PERP, the special case, treated as equity in 19437
		isBondPosition2 = lambda x: x['InvestID'] == 'XS1684793018 Perfshs'
		self.assertEqual( ('Equity', 'Listed equities')
						, getAssetType(blpData, firstOf(isBondPosition2, positions)))

		# The callable bond: BCHINA V3.6 PERP
		isCallableBondPosition = lambda x: x['InvestID'] == 'XS2125922349'
		self.assertEqual( ('Fixed Income', 'Additional Tier 1, Contingent Convertibles')
						, getAssetType(blpData, firstOf(isCallableBondPosition, positions)))

		# The iShares A50 China ETF
		isA50Fund = lambda x: x['InvestID'] == '2823 HK'
		self.assertEqual( ('Fund', 'Exchange Traded Funds')
						, getAssetType(blpData, firstOf(isA50Fund, positions)))

		# The LINK REIT (823 HK is treated as special case)
		isREITFund = lambda x: x['InvestID'] == '823 HK'
		self.assertEqual( ('Equity', 'Listed equities')
						, getAssetType(blpData, firstOf(isREITFund, positions)))

	def testAverageRating(self):
		positions = getPortfolioPositions('19437', '20200429', 'test')
		blpData = getBlpData('20200429', 'test')

		securitiesWithRatings = compose(
			list
		  , partial(map, lambda p: (getAverageRatingScore(blpData, p), p))
		  , partial(map, lambda t: t[1])
		  , partial(filter, lambda t: ratingsApplicable(t[0]))
		  , lambda blpData, positions: \
		  		map(lambda p: (getAssetType(blpData, p), p), positions)
		)(blpData, positions)

		# There are 154 bonds (155 bonds, but one with special case override)
		self.assertEqual(154, len(securitiesWithRatings))

		# Has 3 credit ratings
		self.assertEqual(11, firstOf( lambda t: t[1]['InvestID'] == 'XS2114413565'
									, securitiesWithRatings)[0])

		# Has 2 credit ratings
		self.assertEqual(11, firstOf( lambda t: t[1]['InvestID'] == 'USY70902AB04'
									, securitiesWithRatings)[0])

		# Has 1 credit ratings
		self.assertEqual(13, firstOf( lambda t: t[1]['InvestID'] == 'XS2127809528'
									, securitiesWithRatings)[0])

		# Has no credit ratings
		self.assertEqual(0, firstOf( lambda t: t[1]['InvestID'] == 'XS2021226985'
									, securitiesWithRatings)[0])


from racetrack import Racetrack
import pygame
import sys
import argparse
import time
import LAO_search
import BLAO_search

import pygame.locals

class Application:
    def __init__(self, filename: str, scale=20, fps=30):
        self.running = True
        self.displaySurface = None
        self.scale = scale
        self.fps = fps
        self.windowTitle = "Racetrack MDP: " + filename

        self.track = Racetrack(filename)
        self.dim = self.track.get_dimensions()

        self.windowHeight = self.dim[0] * self.scale
        self.windowWidth = self.dim[1] * self.scale

        self.blockSizeX = int(self.windowWidth / self.dim[1])
        self.blockSizeY = int(self.windowHeight / self.dim[0])

    def execute(self, search_method: str, save: str):
        bidirectional = False
        path_end = None
        if bidirectional:
            path, path_end = BLAO_search.search(self.track, search_method)
        else:
            path = LAO_search.search(self.track, search_method)

        pygame.init()
        self.displaySurface = pygame.display.set_mode((self.windowWidth, self.windowHeight), pygame.HWSURFACE)
        self.displaySurface.fill((255, 255, 255))
        pygame.display.flip()
        pygame.display.set_caption(self.windowTitle)

        print("Results")
        #print("Path Length:", len(path))
        #print("States Explored:", statesExplored)

        self.drawExplored(path, set())
        self.drawPolicy(path, set())
        self.drawPolicy(path_end, set(), True)
        self.drawTrack()
        self.drawStart()
        self.drawObjective()

        pygame.display.flip()
        if save is not None:
            pygame.image.save(self.displaySurface, save)
            self.running = False

        clock = pygame.time.Clock()

        while self.running:
            pygame.event.pump()
            keys = pygame.key.get_pressed()
            clock.tick(self.fps)

            if any(keys):
                raise SystemExit

    def getColor(self, pathLength, index):
        step = 255 / pathLength
        green = index * step
        red = 255 - green
        return red, green, 0

    def drawPolicy(self, path, seen_nodes: set, invert=False, optimum=True):
        if path is None:
            return
        seen_nodes.add(path.state[:2].tobytes())
        p = path
        if invert:
            if optimum:
                color = (128, 0, 255)
            else:
                color = (128, 0, 0)
        else:
            if optimum:
                color = (0, 255, 255)
            else:
                color = (0, 255, 0)

        self.drawArrow(p.state[0], p.state[1], p.state[2], p.state[3], color)

        actions = p.get_recommended_actions()
        if actions is not None:
            success, fail = actions
            if success.state[:2].tobytes() not in seen_nodes:
                seen_nodes = self.drawPolicy(success, seen_nodes, invert, optimum)
            optimum = False
            if fail.state[:2].tobytes() not in seen_nodes:
                seen_nodes = self.drawPolicy(fail, seen_nodes, invert, optimum)
        return seen_nodes

    def drawExplored(self, path, seen_nodes: set):
        if path is None:
            return
        seen_nodes.add(path.state[:2].tobytes())
        p = path
        color = (30, 30, 30)

        self.drawArrow(p.state[0], p.state[1], p.state[2], p.state[3], color)

        for actions in p.children:
            if actions is not None:
                success, fail = actions
                if success.state[:2].tobytes() not in seen_nodes:
                    seen_nodes = self.drawExplored(success, seen_nodes)
                if fail.state[:2].tobytes() not in seen_nodes:
                    seen_nodes = self.drawExplored(fail, seen_nodes)
        return seen_nodes

    # Simple wrapper for drawing a wall as a rectangle
    def drawWall(self, row, col):
        pygame.draw.rect(self.displaySurface, (0, 0, 0), (col * self.blockSizeX, row * self.blockSizeY, self.blockSizeX, self.blockSizeY), 0)

    # Simple wrapper for drawing a circle
    def drawCircle(self, row, col, color, radius=None):
        if radius is None:
            radius = min(self.blockSizeX, self.blockSizeY) / 4
        pygame.draw.circle(self.displaySurface, color, (int(col * self.blockSizeX + self.blockSizeX / 2), int(row * self.blockSizeY + self.blockSizeY / 2)), int(radius))

    def drawSquare(self, row, col, color):
        pygame.draw.rect(self.displaySurface, color, (col * self.blockSizeX, row * self.blockSizeY, self.blockSizeX, self.blockSizeY), 0)

    # Draws the objectives to the display context
    def drawObjective(self):
        for obj in self.track.get_objectives():
            self.drawCircle(obj[0], obj[1], (0, 0, 0))

    # Draws start location of path
    def drawStart(self):
        for state in self.track.get_start():
            pygame.draw.rect(self.displaySurface, (0, 0, 255), (state[1] * self.blockSizeX + self.blockSizeX / 4, state[0] * self.blockSizeY + self.blockSizeY / 4, self.blockSizeX * 0.5, self.blockSizeY * 0.5), 0)

    # Draws arrow
    def drawArrow(self, row, col, row_accel, col_accel, color):
        pygame.draw.rect(self.displaySurface, color, (col * self.blockSizeX + self.blockSizeX / 4, row * self.blockSizeY + self.blockSizeY / 4, self.blockSizeX * 0.5, self.blockSizeY * 0.5), 0)

    # Draws the full maze to the display context
    def drawTrack(self):
        for row in range(self.dim[0]):
            for col in range(self.dim[1]):
                if self.track.is_wall(row, col):
                    self.drawWall(row, col)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    parser.add_argument('--filename', default="small_track.txt", type=str,
                        help='path to racetrack file - default ')
    parser.add_argument('--method', dest="search", type=str, default="dblao",
                        choices=["lao", "blao", "dblao"],
                        help='search method - default dblao')
    parser.add_argument('--scale', dest="scale", type=int, default=20,
                        help='scale - default: 20')
    parser.add_argument('--fps', dest="fps", type=int, default=30,
                        help='fps for the display - default 30')
    parser.add_argument('--save', dest="save", type=str, default=None,
                        help='save output to image file - default not saved')

    args = parser.parse_args()
    app = Application(args.filename, args.scale, args.fps)
    app.execute(args.search, args.save)


from pathlib import Path

from tkinter import Tk, Canvas, Entry, Text, Button, PhotoImage, ttk, END, messagebox
from tkcalendar import DateEntry
import tkinter.font as tkFont
import staff, checkin, checkout, shift
from tkinter.messagebox import askyesno
import openpyxl

class Schedule(ttk.Frame):
    def __init__(self,frame):
        super().__init__(frame)

        OUTPUT_PATH = Path(__file__).parent
        ASSETS_PATH = OUTPUT_PATH / Path(r".\assets\frame4")

        def relative_to_assets(path: str) -> Path:
            return ASSETS_PATH / Path(path)

        self.canvas = Canvas(
            self,
            bg = "#FFFFFF",
            height = 650,
            width = 650,
            bd = 0,
            highlightthickness = 0,
            relief = "ridge"
        )

        self.canvas.place(x = 0, y = 0)
        self.canvas.create_rectangle(
            431.0,
            48.0,
            1081.0,
            698.0,
            fill="#FFFFFF",
            outline="")

        self.canvas.create_text(
            280.0,
            65.0,
            anchor="nw",
            text="LỊCH LÀM",
            fill="#1E1E1E",
            font=("Inter SemiBold", 20 * -1)
        )

        self.canvas.create_text(
            100.0,
            105.0,
            anchor="nw",
            text="Từ",
            fill="#1E1E1E",
            font=("Inter SemiBold", 20 * -1)
        )
        self.start = DateEntry(
            self,
            state="readonly",
            date_pattern="yyyy-MM-dd"
        )
        self.start.place(
            x=140.0,
            y=105.0,
            width=80.0,
            height=30.0
        )

        self.canvas.create_text(
            240.0,
            105.0,
            anchor="nw",
            text="Đến",
            fill="#1E1E1E",
            font=("Inter SemiBold", 20 * -1)
        )
        self.end = DateEntry(
            self,
            state="readonly",
            date_pattern="yyyy-MM-dd"
        )
        self.end.place(
            x=290.0,
            y=105.0,
            width=80.0,
            height=30.0
        )

        self.button_image_4 = PhotoImage(
            file=relative_to_assets("button_4.png"))
        self.button_4 = Button(
            self,
            image=self.button_image_4,
            borderwidth=0,
            highlightthickness=0,
            command=lambda: self.set_table(self.start.get(), self.end.get()),
            relief="flat"
        )
        self.button_4.place(
            x=390.0,
            y=105.0,
            width=65.0,
            height=30.0
        )

        self.button_image_5 = PhotoImage(
            file=relative_to_assets("button_5.png"))
        self.button_5 = Button(
            self,
            image=self.button_image_5,
            borderwidth=0,
            highlightthickness=0,
            command=lambda: self.set_form(),
            relief="flat"
        )
        self.button_5.place(
            x=470.0,
            y=105.0,
            width=65.0,
            height=30.0
        )

        self.style = ttk.Style()
        self.style.configure("myStyle.Treeview", font=('Calibri', 10), rowheight=30) # Modify the font of the body
        self.style.configure("myStyle.Treeview.Heading", font=('Calibri', 10)) # Modify the font of the headings
        self.style.layout("mystyle.Treeview", [('mystyle.Treeview.treearea', {'sticky': 'nswe'})])

        self.scheduleTable = ttk.Treeview(self)

        self.entry_image_5 = PhotoImage(
            file=relative_to_assets("entry_1.png"))
        self.entry_bg_5 = self.canvas.create_image(
            300.0,
            170.0,
            image=self.entry_image_5
        )
        self.entry_5 = Entry(
            self,
            bd=0,
            bg="#FFFFFF",
            fg="#000716",
            highlightthickness=0
        )
        self.entry_5.place(
            x=104.0,
            y=156.0,
            width=310.0,
            height=26.0
        )
        self.button_image_3 = PhotoImage(
            file=relative_to_assets("button_8.png"))
        self.button_3 = Button(
            self,
            image=self.button_image_3,
            borderwidth=0,
            highlightthickness=0,
            command=lambda: self.find_staff(self.entry_5.get()),
            relief="flat"
        )
        self.button_3.place(
            x=466.0,
            y=155.0,
            width=80.0,
            height=30.0
        )

        self.canvas.create_text(
            136.0,  # 567
            219.0,
            anchor="nw",
            text="Mã cá nhân",
            fill="#000000",
            font=("Inter SemiBold", 15 * -1)
        )

        self.entry_image_1 = PhotoImage(
            file=relative_to_assets("entry_2.png"))
        self.entry_bg_1 = self.canvas.create_image(
            386,
            229.0,
            image=self.entry_image_1
        )
        self.entry_1 = Entry(
            self,
            bd=0,
            bg="#FFFFFF",
            fg="#000716",
            highlightthickness=0,
        )
        self.entry_1.place(
            x=260,
            y=215.0,
            width=253.0,
            height=28.0
        )

        optionsH = ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23"]
        optionsM = ["00","15","30","45"]

        self.entry_image_2 = PhotoImage(
            file=relative_to_assets("entry_2.png"))
        self.entry_bg_2 = self.canvas.create_image(
            386,
            264.0,
            image=self.entry_image_2
        )
        self.entry_2 = ttk.Combobox(
            self,
            state="readonly",
            values=optionsH,
        )
        self.entry_2.place(
            x=260.0,
            y=250.0,
            width=126.0,
            height=28.0
        )
        self.entry_2_1 = ttk.Combobox(
            self,
            state="readonly",
            values=optionsM,
        )
        self.entry_2_1.place(
            x=386.0,
            y=250.0,
            width=126.0,
            height=28.0
        )

        self.canvas.create_text(
            136.0,
            254.0,
            anchor="nw",
            text="Từ",
            fill="#000000",
            font=("Inter SemiBold", 15 * -1)
        )

        self.entry_image_3 = PhotoImage(
            file=relative_to_assets("entry_2.png"))
        self.entry_bg_3 = self.canvas.create_image(
            386,
            299.0,
            image=self.entry_image_3
        )
        self.entry_3 = ttk.Combobox(
            self,
            state="readonly",
            values=optionsH,
        )
        self.entry_3.place(
            x=260,
            y=285.0,
            width=126.0,
            height=28.0
        )
        self.entry_3_1 = ttk.Combobox(
            self,
            state="readonly",
            values=optionsM,
        )
        self.entry_3_1.place(
            x=386,
            y=285.0,
            width=126.0,
            height=28.0
        )

        self.canvas.create_text(
            136.0,
            289.0,
            anchor="nw",
            text="Đến",
            fill="#000000",
            font=("Inter SemiBold", 15 * -1)
        )

        self.entry_image_4 = PhotoImage(
            file=relative_to_assets("entry_2.png"))
        self.entry_bg_4 = self.canvas.create_image(
            386,
            334.0,
            image=self.entry_image_3
        )
        self.entry_4 = DateEntry(
            self,
            state="readonly",
            date_pattern="yyyy-MM-dd"
        )
        self.entry_4.place(
            x=260,
            y=320.0,
            width=253.0,
            height=28.0
        )

        self.button_image_6 = PhotoImage(
            file=relative_to_assets("button_6.png"))
        self.button_6 = Button(
            self,
            image=self.button_image_6,
            borderwidth=0,
            highlightthickness=0,
            relief="flat"
        )
        self.button_6.place(
            x=290.0,
            y=369.0,
            width=70.0,
            height=30.0
        )
        self.canvas.create_text(
            136.0,
            324.0,
            anchor="nw",
            text="Ngày làm",
            fill="#000000",
            font=("Inter SemiBold", 15 * -1)
        )

        self.button_image_7 = PhotoImage(
            file=relative_to_assets("button_7.png"))
        self.button_7 = Button(
            self,
            image=self.button_image_7,
            borderwidth=0,
            highlightthickness=0,
            command=lambda: self.export(),
            relief="flat"
        )

        #right
        self.canvas.create_rectangle(
            527.0,
            199.0,
            529.0,
            409.0,
            fill="#000000",
            outline="")

        #bot
        self.canvas.create_rectangle(
            123.0,
            408.0,
            527.0,
            410.0,
            fill="#000000",
            outline="")

        #left
        self.canvas.create_rectangle(
            123.0,
            199.0,
            125.0,
            408.0,
            fill="#000000",
            outline="")

        #top
        self.canvas.create_rectangle(
            123.0,
            198.0,
            527.0,
            200.0,
            fill="#000000",
            outline="")

    def set_table(self, start, end):
        self.scheduleTable.place_forget()
        day = shift.select_day(start, end)
        id = staff.select_id()
        self.columns = ["ID", "FULL NAME"]
        self.rows = []
        for i in range(len(day)):
            self.columns.append(day[i][0].strftime("%Y-%m-%d"))
        self.scheduleTable = ttk.Treeview(self, columns=tuple(self.columns), show="headings", style="myStyle.Treeview")
        for i in range(len(self.columns)):
            self.scheduleTable.heading(i, text=self.columns[i])
            if i == 0:
                self.scheduleTable.column(i, width=70, anchor='center')
            else:
                self.scheduleTable.column(i, width=150, anchor='center')
        self.set_data_table(self.columns,id)
        self.vsbx = ttk.Scrollbar(self, orient="horizontal", command=self.scheduleTable.xview)
        self.vsbx.place(x=0,y=595,width=610)
        self.vsby = ttk.Scrollbar(self, orient="vertical", command=self.scheduleTable.yview)
        self.vsby.place(x=622,y=150,height=463)
        self.scheduleTable.configure(yscrollcommand=self.vsby.set, xscrollcommand=self.vsbx.set)
        self.scheduleTable.place(x=0,y=150, width=610,height=440)
        self.button_7.place(
            x=250.0,
            y=620.0,
            width=130.0,
            height=30.0
        )

    def set_data_table(self,day,id):
        for i in id:
            row = [i, staff.select_one_name(i)]
            for d in day:
                if d != "ID" and d != "FULL NAME":
                    row.append(shift.select_shift(i,d))
            self.scheduleTable.insert('','end',values=row)

    def set_form(self):
        self.scheduleTable.place_forget()
        self.vsbx.place_forget()
        self.vsby.place_forget()
        self.button_7.place_forget()

        # =================================================

    def find_staff(self,input):
        if input in staff.select_id():
            self.entry_1.delete(0, END)
            self.entry_1.insert(0, input)
            self.button_6.config(command=lambda: self.insert())
        else:
            messagebox.showerror("Lỗi","Không tìm thấy nhân viên")
            self.entry_5.delete(0, END)

    def insert(self):
        answer = askyesno(title="Confirm insert",
                        message='Bạn chắc chắn chứ ?')

        if answer:
            if self.entry_1.get() == '' or self.entry_2.get() == '' or self.entry_4.get() == '':

                messagebox.showerror(
                    'Invalid input', 'Thông tin không được để trống')
                return
            if not self.entry_1.get().isdigit():
                messagebox.showerror(
                    'Invalid input', 'Mã cá nhân phải là số')
                return
            try:
                inTime = "{}:{}:00".format(self.entry_2.get(),self.entry_2_1.get())
                outTime = "{}:{}:00".format(self.entry_3.get(),self.entry_3_1.get())
                a = shift.Shift(self.entry_1.get(),self.entry_4.get(),inTime=inTime,outTime=outTime)
                a.insert()
            except ValueError:
                messagebox.showerror(
                    'ERROR ⚠', 'Mã trùng')
                return
            messagebox.showinfo('Done!!!', "Đã thêm thành công")

            self.entry_1.delete(0, END)
            self.entry_2.delete(0, END)

    def export(self):
        workbook = openpyxl.Workbook()
        worksheet = workbook.active
        # Ghi heading vào file Excel
        for i in range(len(self.columns)):
            worksheet.cell(row=1,column=i+1,value=self.columns[i])
        for i, row in enumerate(self.scheduleTable.get_children()):
            values = self.scheduleTable.item(row)["values"]
            for j in range(len(values)):
                worksheet.cell(row=i+2,column=j+1,value=values[j])
        workbook.save("{}-{}.xlsx".format(self.columns[2],self.columns[len(self.columns)-1]))


import requests
import json
import pytest

url = 'https://petstore.swagger.io/v2/user'

def base(**kwargs):
    with open(str(kwargs['json_file'])) as f:
        user = json.load(f)[str(kwargs['user'])]

        obj_post = requests.post(url, json=user)
        assert obj_post.status_code == int(kwargs['post_status']), \
            f'ERROR POST: url {url}.\nData {user}.\nStatus error {obj_post.status_code}'

        obj_get = requests.get(f'{url}/{user["username"]}')
        assert obj_get.status_code == int(kwargs['get_status']), \
            f'ERROR GET: url {url}. \nUsername {user["username"]}. \nStatus error {obj_get.status_code}'

# POST REQUESTS
def test_create_user():
    base(
        json_file='post_response.json',
        user='create_user',
        post_status=200,
        get_status=200
    )

def test_create_user_null():
    base(
        json_file='post_response.json',
        user='create_user_null',
        post_status=500,
        get_status=404
    )

def test_create_user_id_str():
    base(
        json_file='post_response.json',
        user='create_user_id_str',
        post_status=500,
        get_status=404
    )

def test_create_user_id_minus():
    base(
        json_file='post_response.json',
        user='create_user_id_minus',
        post_status=500,
        get_status=404
    )

def test_create_user_all_info_int():
    base(
        json_file='post_response.json',
        user='create_user_all_info_int',
        post_status=500,
        get_status=404
    )

def test_create_user_more_then_limit_user_status():
    base(
        json_file='post_response.json',
        user='create_user_more_then_limit_userStatus',
        post_status=500,
        get_status=404
    )

def test_create_user_boolean_id():
    base(
        json_file='post_response.json',
        user='create_user_boolean_id',
        post_status=500,
        get_status=404
    )

def test_create_user_boolean_user_status():
    base(
        json_file='post_response.json',
        user='create_user_boolean_userStatus',
        post_status=500,
        get_status=404
    )

def test_create_user_empty_username():
    base(
        json_file='post_response.json',
        user='create_user_empty_username',
        post_status=400,
        get_status=404
    )

def test_create_user_only_username():
    base(
        json_file='post_response.json',
        user='create_user_only_username',
        post_status=400,
        get_status=404
    )

if __name__ == '__main__':
    test_create_user()
    # test_create_user_null()
    # test_create_user_id_str()
    # test_create_user_all_info_int()
    # test_create_user_more_then_limit_user_status()
    # test_create_user_boolean_id()
    # test_create_user_boolean_userStatus()
    # test_create_user_empty_username()
    # test_create_user_only_username()
    # pass


import numpy as np
import cv2
import imutils

image= cv2.imread("/Users/sam/All-Program/App-DataSet/Image-Processing/sam.jpg")
cv2.imshow("Original_image", image)
cv2.waitKey(0)

## Our Image is too large, hence we first shorten it using resizing
resized=imutils.resize(image, height=500)  # Passing the new height of the image , the width is automatically calculated in resize method of the imutil package
cv2.imshow("resized image", resized)
cv2.waitKey(0)
image=resized

## Now we determine the pixels that contains the head
cropped= image[7:230, 25:235]  # basically we display the pixel in that particulae area
# nuply array takes array input as (height * width)
cv2.imshow("cropped image", cropped)
cv2.waitKey(0)

## Now we perform Masking from center
# Now we perform masking on the original image, Here we would manually provide the pixels
# First we create a rectangle, because faces are suppose to be of rectangle size
# First lets just define a rectangle white in color and mask it in the center
mask=np.zeros(image.shape[:2], dtype="uint8")  # we create a mask with same array size as that of our image
(cx, cy)= (image.shape[1]/2, image.shape[0]/2) # we take the center point
cv2.rectangle(mask, (cx-105,cy-111), (cx+105,cy+111), 255,-1)  # (width(cx) * height(cy)
cv2.imshow("Mask_rectangle", mask)
cv2.waitKey(0)

masked=cv2.bitwise_and(image, image,mask=mask)
cv2.imshow("Masked_from_center", masked)
cv2.waitKey(0)

## Now we perform Masking of face
# Now we will shift the center and extract the face
mask=np.zeros(image.shape[:2], dtype="uint8")  # we create a mask with same array size as that of our image
(cx, cy)= (105, 111) # we take the center point
cv2.rectangle(mask, (cx-(105-25),cy-(111-7)), (cx+(235-105),cy+(230-111)), 255,-1) # (width(cx) * height(cy)
cv2.imshow("Mask_rectangle", mask)
cv2.waitKey(0)

masked=cv2.bitwise_and(image, image,mask=mask)
cv2.imshow("Masked_from_center", masked)
cv2.waitKey(0)

cv2.destroyAllWindows()


# Stdlib imports
# Core Django imports
from crispy_forms.bootstrap import Field, FieldWithButtons, StrictButton
from crispy_forms.layout import (
    HTML,
    Button,
    ButtonHolder,
    Column,
    Hidden,
    Layout,
    Row,
    Submit,
)
from django import forms
from django.contrib.gis.geos import Point as geoPoint
from django.core.exceptions import ValidationError
from django.urls import reverse_lazy
from django.utils.http import urlencode

# Third-party app imports
# Imports from my apps
from src.campaigns.models import Campaign
from src.model_grids.models import ModelGrid
from src.plots.models import StationsPlot
from src.utils.mixins import CrispyMixin
from src.utils.util import M2MSelect
from src.utils.util import degree_sign as deg

from .models import Station

class StationForm(CrispyMixin, forms.ModelForm):
    current_url = "stations:create"

    longitude = forms.DecimalField(
        min_value=-180,
        max_value=180,
        help_text=f"{deg}E",
        max_digits=9,
        decimal_places=6,
    )
    latitude = forms.DecimalField(
        min_value=-90,
        max_value=90,
        help_text=f"{deg}N",
        max_digits=8,
        decimal_places=6,
        # Pb: 'step' force the number of mandatory decimal
        # widget=forms.NumberInput(
        #     attrs={'step': 0.01,}
        # ),
    )

    altitude = forms.DecimalField(
        min_value=0,
        help_text="m",
        max_digits=11,
        decimal_places=6,
    )

    plots = forms.ModelMultipleChoiceField(
        required=False,
        queryset=StationsPlot.objects.all(),
        widget=forms.CheckboxSelectMultiple(
            attrs={"class": "form-check", "style": "list-style:none;"}
        ),
    )

    campaigns = forms.ModelMultipleChoiceField(
        required=False,
        queryset=Campaign.objects.all(),
        widget=forms.CheckboxSelectMultiple(
            attrs={"class": "form-check", "style": "list-style:none;"}
        ),
    )

    start_datetime = forms.DateTimeField(
        widget=forms.widgets.DateTimeInput(attrs={"placeholder": "YYYY-MM-DD HH:MM"})
    )
    end_datetime = forms.DateTimeField(
        widget=forms.widgets.DateTimeInput(attrs={"placeholder": "YYYY-MM-DD HH:MM"})
    )

    class Meta:
        model = Station
        fields = [
            "name",
            "longitude",
            "latitude",
            "altitude",
            "is_active",
            "station_id",
            "wmo_id",
            "description",
            "margin",
            "plots",
            "campaigns",
            "uses_flexpart",
            "start_datetime",
            "end_datetime",
            "alt_lower",
            "alt_upper",
            "alt_unit",
            "numb_part",
            "xmass",
            "number_grid",
        ]

    def _init_helper_layout(self):
        """initialise crispy layout"""
        self.helper.error_text_inline = False
        self.helper.layout = Layout(
            Hidden("next", "{{ request.GET.path }}"),
            Row(
                Column(
                    Field("name"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("station_id"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("wmo_id"),
                    css_class="form-group col-md-4 mb-0",
                ),
                css_class="form-row",
            ),
            HTML("<legend>Coordinates</legend>"),
            Row(
                Column(
                    Field("latitude"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("longitude"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("altitude"),
                    css_class="form-group col-md-4 mb-0",
                ),
                css_class="form-row",
            ),
            # Field("name"),
            # Fieldset("Coordinates", "latitude", "longitude", "altitude"),
            FieldWithButtons(
                "margin",
                StrictButton(
                    "",
                    css_class="btn-success",
                    onclick='window.location.href = "{}?{}";'.format(
                        reverse_lazy("margins:list"),
                        urlencode({"next": reverse_lazy(self.current_url)}),
                    ),
                ),
            ),
            Field("is_active"),
            Field("uses_flexpart"),
            Field("description", rows="4"),
            HTML("
"),
            HTML("
"),
            Field("campaigns"),
            HTML("
"),
            HTML("
"),
            HTML("
"),
            Field("plots"),
            HTML("
"),
            HTML("
"),
            HTML("
"),
            Row(
                Column(
                    Field("start_datetime"),
                    css_class="form-group col-md-6 mb-0",
                ),
                Column(
                    Field("end_datetime"),
                    css_class="form-group col-md-6 mb-0",
                ),
                css_class="form-row",
            ),
            Row(
                Column(
                    Field("alt_lower"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("alt_upper"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("alt_unit"),
                    css_class="form-group col-md-4 mb-0",
                ),
                css_class="form-row",
            ),
            Row(
                Column(
                    Field("numb_part"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("xmass"),
                    css_class="form-group col-md-4 mb-0",
                ),
                Column(
                    Field("number_grid"),
                    css_class="form-group col-md-4 mb-0",
                ),
                css_class="form-row",
            ),
            HTML("
"),
            # HTML(""),
            HTML("
"),
            ButtonHolder(
                Submit("submit", "Submit", css_class="btn-success"),
                Button(
                    "cancel",
                    "Cancel",
                    css_class="btn-primary",
                    onclick="history.go(-1);",
                ),
            ),
        )

    def _custom_helper(self):
        """customize crispy form"""
        # Sort plots alphabetically
        self.fields["plots"].label = "Available plots"
        self.fields["plots"].queryset = StationsPlot.objects.order_by("name")
        # rename label
        self.fields["start_datetime"].label = "Starting date"
        self.fields["end_datetime"].label = "Ending date"
        self.fields["alt_lower"].label = "Lower altitude"
        self.fields["alt_upper"].label = "Upper altitude"
        self.fields["alt_unit"].label = "Unit"
        self.fields["numb_part"].label = "Particles"
        self.fields["xmass"].label = "Total mass"
        # fields not required
        self.fields["start_datetime"].required = False
        self.fields["end_datetime"].required = False
        self.fields["alt_lower"].required = False
        self.fields["alt_upper"].required = False
        self.fields["alt_unit"].required = False
        self.fields["numb_part"].required = False
        self.fields["xmass"].required = False
        self.fields["number_grid"].required = False

    def clean(self):
        """check station is located inside one the registered forecast"""
        cleaned_data = super().clean()
        # capitalize name
        name = cleaned_data.get("name")
        lat = float(cleaned_data.get("latitude", 0))
        lon = float(cleaned_data.get("longitude", 0))
        alt = float(cleaned_data.get("altitude", 0))
        point = geoPoint(lon, lat, alt)

        model_grids = ModelGrid.objects.all()

        if len(model_grids) == 0:
            raise ValidationError(
                "No Weather Forecast registered. You must have registered at least one before assigning station"
            )

        for model_grid in model_grids:
            poly = model_grid.geom
            # valid = poly.contains(point)
            # valid = poly.covers(point)
            valid = point.intersects(poly)
            if valid:
                print(f"Station {name} is in {model_grid.name}")
                break

        if not valid:
            raise ValidationError(
                "Station is not inside any ModelGrid registered.",
            )

        uses_flexpart = cleaned_data.get("uses_flexpart")

        alt_lower = cleaned_data.get("alt_lower")
        alt_upper = cleaned_data.get("alt_upper")

        if uses_flexpart:
            if not alt_lower and alt_lower != 0:
                raise ValidationError("Lower altitude is a required field.")
            if not alt_upper and alt_upper != 0:
                raise ValidationError("Upper altitude is a required field.")
            if alt_upper < alt_lower:
                raise ValidationError(
                    "Flexpart release altitudes aren't sorted in ascending order.",
                )

        start_datetime = cleaned_data.get("start_datetime")
        end_datetime = cleaned_data.get("end_datetime")

        if uses_flexpart:
            if start_datetime and end_datetime and end_datetime < start_datetime:
                raise ValidationError(
                    "Flexpart release dates aren't sorted in ascending order.",
                )

        numb_part = cleaned_data.get("numb_part")
        if not isinstance(numb_part, int):
            raise ValidationError(
                "Flexpart number of particles must be an integer.",
            )

        xmass = cleaned_data.get("xmass")
        if not isinstance(xmass, int):
            raise ValidationError(
                "Flexpart xmass must be an integer.",
            )

        number_grid = cleaned_data.get("number_grid")
        if not isinstance(number_grid, int):
            raise ValidationError(
                "Flexpart number_grid must be an integer.",
            )

class StationUpdateForm(StationForm):
    def _custom_helper(self):
        """customize crispy form"""
        super()._custom_helper()
        # change some field
        self.fields["name"].disabled = True

class StationCampaignForm(CrispyMixin, forms.ModelForm):
    current_url = "stations:redirect"

    campaigns = forms.ModelMultipleChoiceField(
        required=False,
        queryset=Campaign.objects.all(),
        widget=M2MSelect,
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["campaigns"].empty_label = "---------"

    class Meta:
        model = Station
        fields = [
            "campaigns",
        ]

    def _init_helper_layout(self):
        """initialise crispy layout"""
        self.helper.attrs = {
            "data-change-campaign-url": reverse_lazy("stations:change_campaign"),
        }
        self.helper.error_text_inline = False
        self.helper.form_show_labels = False
        self.helper.layout = Layout(
            Hidden("next", "{{ request.GET.path }}"),
            FieldWithButtons(
                "campaigns",
                StrictButton(
                    "",
                    css_class="btn-success",
                    onclick='window.location.href = "{}?{}";'.format(
                        reverse_lazy("campaigns:list"),
                        urlencode({"next": reverse_lazy(self.current_url)}),
                    ),
                ),
                css_id="change_campaign_id",
            ),
        )


import cv2
import numpy as np
import os
import sys
import tensorflow as tf
import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split

EPOCHS = 10
IMG_WIDTH = 30
IMG_HEIGHT = 30
NUM_CATEGORIES = 43
TEST_SIZE = 0.4

def main():

    # Check command-line arguments
    if len(sys.argv) not in [2, 3]:
        sys.exit("Usage: python traffic.py data_directory [model.h5]")

    # Get image arrays and labels for all image files
    print('Loading data...')
    images, labels = load_data(sys.argv[1])

    # Split data into training and testing sets
    labels = tf.keras.utils.to_categorical(labels)
    x_train, x_test, y_train, y_test = train_test_split(
        np.array(images), np.array(labels), test_size=TEST_SIZE
    )

    # Get a compiled neural network
    print('Loading Model...')
    model = get_model()

    # Fit model on training data
    history = model.fit(x_train, y_train, epochs=EPOCHS, batch_size=32)
    model_logs(history)
    # Evaluate neural network performance
    model.evaluate(x_test,  y_test, verbose=2)

    # Save model to file
    if len(sys.argv) == 3:
        filename = sys.argv[2]
        model.save(filename)
        print(f"Model saved to {filename}.")

def load_data(data_dir):
    """
    Load image data from directory `data_dir`.

    Assume `data_dir` has one directory named after each category, numbered
    0 through NUM_CATEGORIES - 1. Inside each category directory will be some
    number of image files.

    Return tuple `(images, labels)`. `images` should be a list of all
    of the images in the data directory, where each image is formatted as a
    numpy ndarray with dimensions IMG_WIDTH x IMG_HEIGHT x 3. `labels` should
    be a list of integer labels, representing the categories for each of the
    corresponding `images`.
    """
    images, labels = [], []
    fnames = os.listdir(data_dir)
    for filename in fnames:
        print(f'Reading file {filename}')
        img_folder = os.path.join(data_dir, filename)
        for img_path in os.listdir(img_folder):
            img = cv2.imread(os.path.join(img_folder, img_path))
            if img is not None:
                images.append(cv2.resize(img, (IMG_HEIGHT, IMG_WIDTH)))
                labels.append(int(filename))

    return (images, labels)

def get_model():
    """
    Returns a compiled convolutional neural network model. Assume that the
    `input_shape` of the first layer is `(IMG_WIDTH, IMG_HEIGHT, 3)`.
    The output layer should have `NUM_CATEGORIES` units, one for each category.
    """
    model = tf.keras.Sequential([
        tf.keras.layers.Conv2D(64, (3, 3), activation='relu', input_shape = (IMG_HEIGHT, IMG_WIDTH, 3)),
        tf.keras.layers.MaxPool2D((2, 2)),

        tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),
        tf.keras.layers.MaxPool2D((2, 2)),

        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(64, activation='relu'),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(NUM_CATEGORIES, activation='softmax')
    ])

    model.compile(loss='categorical_crossentropy',
    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
    metrics=['accuracy'])

    return model

def model_logs(history):
    loss = history.history['loss']
    accuracy = history.history['accuracy']
    fig, axes = plt.subplots(2, sharex=True, figsize=(12, 8))
    fig.suptitle('Training Metrics')

    axes[0].set_ylabel("Loss", fontsize=14)
    axes[0].plot(loss)

    axes[1].set_ylabel("Accuracy", fontsize=14)
    axes[1].set_xlabel("Epoch", fontsize=14)
    axes[1].plot(accuracy)
    plt.show()

if __name__ == "__main__":
    main()


import os

import mock

from django.test import TestCase
from django.test import override_settings
from pyfakefs.fake_filesystem_unittest import TestCase as FSTestCase

from mii_common import tools
from mii_indexer.factories import MovieRelationFactory, MovieTaggingFactory
from mii_interface.factories import ReportFactory
from mii_interface.views import discrepancies
from mii_rating.models import QuestionAnswer, MovieQuestionSet
from mii_sorter.factories import EpisodeFactory, MovieFactory, SerieFactory, SeasonFactory
from mii_sorter.models import Movie

@override_settings(DESTINATION_FOLDER='/home/destination')
class TestViews(TestCase):
    @classmethod
    def setUpTestData(cls):
        MovieTaggingFactory.create_batch(3)
        MovieRelationFactory.create_batch(5)
        EpisodeFactory.create_batch(6)
        ReportFactory.create_batch(5)
        cls.episode = EpisodeFactory.create()
        cls.report = ReportFactory.create()

    def test_index(self):
        assert self.client.get('/').status_code == 200

    def test_movies(self):
        assert self.client.get('/movies').status_code == 200

    def test_series(self):
        assert self.client.get('/series').status_code == 200

    def test_rate(self):
        assert self.client.get('/rate').status_code == 200

    def test_reports(self):
        assert self.client.get('/reports').status_code == 200

    def test_report(self):
        assert self.client.get('/report/%s/' %
                               self.report.id).status_code == 200

    def test_report_missing(self):
        assert self.client.get('/report/39202390/').status_code == 200

    @mock.patch('mii_unpacker.views.unpack')
    def test_rpc_unpack(self, unpack):
        response = self.client.get('/rpc/unpack')
        assert response.status_code == 200
        assert unpack.delay.called

    @mock.patch('mii_sorter.views.sort')
    def test_rpc_sort(self, sort):
        response = self.client.get('/rpc/sort')
        assert response.status_code == 200
        assert sort.delay.called

    @mock.patch('mii_indexer.views.index_movies')
    def test_rpc_index(self, index):
        response = self.client.get('/rpc/index')
        assert response.status_code == 200
        assert index.delay.called

    @mock.patch('mii_rss.views.recheck_feed_and_download_torrents')
    def test_rpc_rerss(self, index):
        response = self.client.get('/rpc/recheck_rss')
        assert response.status_code == 200
        assert index.delay.called

    @mock.patch('mii_rss.views.check_feed_and_download_torrents')
    def test_rpc_rss(self, rss):
        response = self.client.get('/rpc/rss')
        assert response.status_code == 200
        assert rss.delay.called

    @mock.patch('mii_interface.views.index_movies')
    @mock.patch('mii_interface.views.unpack')
    @mock.patch('mii_interface.views.sort')
    def test_rpc_index(self, sort, unpack, index):
        response = self.client.get('/rpc/unpack_sort_index')
        assert response.status_code == 200
        assert index.si.called
        assert unpack.si.called
        assert sort.si.called

    def test_mii_rating(self):
        movie = MovieFactory.create(seen=None)
        data = {
            'action': 'not_seen',
            'movie_id': movie.id
        }
        response = self.client.post('/rate', data=data)
        assert Movie.objects.get(id=movie.id).seen is False
        assert response.status_code == 200

    @mock.patch('mii_interface.views.remote_play')
    def test_play(self, remote_play):
        self.client.post('/play', data={'episode_id': self.episode.id})
        remote_play.assert_called_with(self.episode.file_path)

@override_settings(DESTINATION_FOLDER='/home/destination')
class TestMiiRating(TestCase):
    def test_mii_rating_save_question(self):
        movie = MovieFactory.create(seen=None)
        data = {
            'action': 'save_movie',
            'movie_id': movie.id,
            'overall': ['9.4'],
        }
        response = self.client.post('/rate', data=data)
        assert MovieQuestionSet.objects.get(movie_id=movie.id)
        assert QuestionAnswer.objects.get(question_type='overall')
        assert response.status_code == 200

    def test_mii_rating_missing_movie(self):
        response = self.client.get('/rate?movie_id=1')
        assert response.status_code == 200

    def test_rate_with_info(self):
        movie = MovieFactory.create(seen=None)
        MovieRelationFactory.create_batch(5, movie=movie)
        MovieTaggingFactory.create_batch(5, movie=movie)
        response = self.client.get('/rate')
        assert response.status_code == 200

@override_settings(DESTINATION_FOLDER='/processed/')
class TestDiscrepancies(FSTestCase, TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.movie_without_path = MovieFactory.create(
            folder_path='dummy_path.mkv')
        cls.movie_with_path = MovieFactory.create(folder_path='path_exists')
        cls.movie_with_different_path = MovieFactory.create(
            title='Match', year=2000, folder_path='path_exists2')
        cls.serie_with_path = SerieFactory.create(name='Serie2')
        cls.season_with_path = SeasonFactory.create(
            serie=cls.serie_with_path, number=1)
        cls.season_without_path = SeasonFactory.create(
            serie=cls.serie_with_path, number=2)
        cls.episode_with_path = EpisodeFactory(
            season=cls.season_with_path, number=1)
        cls.episode_without_path = EpisodeFactory(
            season=cls.season_with_path, number=2)
        cls.serie_without_path = SerieFactory.create(name='Serie3')

    def setUp(self):
        self.setUpPyfakefs()
        dest_dir = tools.make_dir('/processed/')
        movie_dir = tools.make_dir(os.path.join(dest_dir, 'Movies'))
        serie_dir = tools.make_dir(os.path.join(dest_dir, 'Series'))
        all_movie_dir = tools.make_dir(os.path.join(movie_dir, 'All'))
        self.title_folder = tools.make_dir(
            os.path.join(all_movie_dir, 'Title (2014)'))
        self.match_folder = tools.make_dir(
            os.path.join(all_movie_dir, 'Match (2000)'))
        self.serie1_folder = tools.make_dir(os.path.join(serie_dir, 'Serie1'))
        self.serie1_season1_folder = tools.make_dir(
            os.path.join(self.serie1_folder, 'Season 1'))
        self.serie1_season1_ep1_path = self.fs.create_file(os.path.join(
            self.serie1_season1_folder, 'serie.name.s01e01.mkv'), contents='dummy')
        self.serie2_folder = tools.make_dir(os.path.join(serie_dir, 'Serie2'))
        self.season_with_path.folder_path = tools.make_dir(
            os.path.join(self.serie2_folder, 'Season 1'))
        self.season_with_path.save()
        self.episode_with_path.file_path = os.path.join(
            self.season_with_path.folder_path, str(self.episode_with_path))
        self.episode_with_path.save()
        self.fs.create_file(self.episode_with_path.file_path, contents='dummy')
        tools.make_dir('path_exists')
        tools.make_dir('path_exists2')

    @mock.patch('mii_interface.views.render')
    def test_discrepancies(self, render):
        request = mock.MagicMock()
        discrepancies(request)
        render.assert_called_with(
            request,
            mock.ANY,
            {
                'movie_discrepancy': [
                    {'title': self.movie_without_path.title, 'id': self.movie_without_path.id}],
                'folder_discrepancy': [
                    {'folder_exists': True,
                     'movie_folder_exists': True,
                     'movie_id': self.movie_with_different_path.id,
                     'folder': self.match_folder,
                     'movie_folder': self.movie_with_different_path.folder_path
                     },
                    {
                        'folder': self.title_folder
                    }
                ],
                'series_discrepancy': [{'name': 'Serie3', 'id': 2}],
                'seasons_discrepancy': [{'number': '2', 'id': 2}],
                'episodes_discrepancy': [{'title': 'Episode object (2)', 'file_path': '{destination_dir}serie_13.mkv', 'id': 2}],
                'serie_folder_discrepancy': [
                    {'name': 'Serie1',
                     'folder_path': '/processed/Series/Serie1',
                     'seasons': [{
                         'number': '1',
                         'folder_path': '/processed/Series/Serie1/Season 1',
                         'episodes': [
                             {'number': 1,
                              'file_path': '/processed/Series/Serie1/Season 1/serie.name.s01e01.mkv',
                              'file_size': 5}
                         ]
                     }
                     ]
                     }
                ],
            }
        )

    @mock.patch('mii_interface.views.render')
    def test_discrepancies_fixing(self, render):
        fixing_request = mock.MagicMock()
        fixing_request.method = 'POST'
        discrepancies(fixing_request)
        request = mock.MagicMock()
        request.method = 'GET'
        discrepancies(request)
        render.assert_called_with(
            request,
            mock.ANY,
            {
                'movie_discrepancy': [],
                'folder_discrepancy': [],
                'serie_folder_discrepancy': [],
                'episodes_discrepancy': [],
                'seasons_discrepancy': [],
                'series_discrepancy': [],
            }
        )


#!/bin/env python
import csv
import requests
from io import StringIO
import difflib

url = 'https://docs.google.com/spreadsheet/ccc?key='\
      '1H77kqiz1R7xVBRXxjjMCqk_3AMF6P6qIMi6p2FEL_us&output=csv'

response = requests.get(url)
assert response.status_code == 200, 'Wrong status code'
buff = StringIO(response.content.decode("utf-8"))

classes = {}

reader = csv.reader(buff)

rows = list(reader)
del rows[0]

bros = [row[1] for row in rows]
bigs = [row[3] for row in rows]
big_years = [row[2] for row in rows]

for ind, big in enumerate(bigs):
    if big in bros or big == '':
        continue
    print("Big %s for %s: %s" % (big, big_years[ind],
                                 difflib.get_close_matches(big, bros)))


import logging
from datetime import timedelta
from pathlib import Path

logging.basicConfig(filename="telescreen.log", level=logging.DEBUG)

# -----------Slide Sources-----------
# page which lists all the slide-urls
wikipedia_list_page = "Benutzer:Meisam/test"
wikipedia_lang = "de"

# list of the users who are allowed to edit the wikipedia page
whitelist_users = ["Meisam"]

working_directory = Path(".")

# images will be cached here
cache_folder = Path(".")

# the images included in the wikipedia page will be cached here
# the files in this directory will be completely wiped each time the wikipedia page is updated
wikipedia_list_cache = Path(".")

# the images included in the local list files will be cached here
# directory will be completely wiped each time the slides list is updated
local_lists_cache = Path(".")

# will try to download these types if found in a URL
img_extensions = ("jpeg", "jpg", "png", "gif")
# these types be shown directly
web_extensions = ("htm", "html")
# list files be parsed line-by-line
list_extensions = ("txt",)

# -----------Slide configs-----------
# Update the slides list every ***
cache_lifetime = timedelta(hours=1)

# show each slide for *** seconds
slides_refresh_time = 30


#!/usr/bin/env python3
import os,sys,re,yaml,subprocess,pickle
import os.path as osp
import numpy as np
from easydict import EasyDict as edict

trainCommandTemplate = "./tools/train_net.py --iters {} --imdb mnist-{}-default --cfg ./experiments/cfgs/cls_mnist_al.yml --solver ./models/mnist/{}/solver.prototxt --solver_state ./mnist_al_net_{}_iter_{}.solverstate --new_path_to_imageset {} --snapshot_infix {}"
testCommandTemplate = "./tools/test_net.py --imdb mnist-test-default --cfg ./experiments/cfgs/cls_mnist.yml --def ./models/mnist/{}/test.prototxt --net ./output/classification/mnist/mnist_al_net_{}_{}_iter_{}.caffemodel"
trainOriginalCommandTemplate = "./tools/train_net.py --iters {} --imdb mnist-train-default --cfg ./experiments/cfgs/cls_mnist_al.yml --solver ./models/mnist/{}/solver.prototxt"
testOriginalModelCommandTemplate = "./tools/test_net.py --imdb mnist-test-default --cfg ./experiments/cfgs/cls_mnist.yml --def ./models/mnist/{}/test.prototxt --net {}"

def dbprint(item):
    print("DEBUG_PRINT")
    print(item)

def getAlInfo(fn):
    with open(fn, 'r') as f:
        yaml_cfg = edict(yaml.load(f))
    return yaml_cfg

def assignAccuracy(alIds,alSet,acc):
    alSubset = getAlSubsetInfo(alSet)
    for alId in alSubset:
        if alIds[alId] is None: alIds[alId] = []
        alIds[alId].append(acc)

def getIdList(imageSetPath):
    with open(imageSetPath,"r") as f:
        lines = f.readlines()
    ids = [int(line.strip()) for line in lines]
    return ids

def runCommandProcess(setCommand):
    modelProc = subprocess.Popen(setCommand.split(' '),stdout=subprocess.PIPE)
    output_b,isSuccess = modelProc.communicate()
    assert isSuccess is None, print("ERROR; command failed.")
    output = output_b.decode('utf-8')
    return output

def testOriginalModel(netPath,modelType):
    setTestCommand = testOriginalModelCommandTemplate.format(modelType,netPath)
    return testModel(setTestCommand)

def trainOriginalModel(nIters,modelType):
    setTrainCommand = trainOriginalCommandTemplate.format(nIters,modelType)
    output = trainModel(setTrainCommand)
    return findCaffemodelSnapshot(output)

def trainModel(setTrainCommand):
    print(setTrainCommand)
    return runCommandProcess(setTrainCommand)

def findCaffemodelSnapshot(output):
    findName = r"Wrote snapshot to: (?P<name>.*)"
    trainedModelName = re.findall(findName,output)[-1]
    return trainedModelName

def testModel(setTestCommand):
    print(setTestCommand)
    output = runCommandProcess(setTestCommand)
    findAcc = r"Accuracy: (?P<acc>[0-9]+\.[0-9]+)"
    acc = float(re.findall(findAcc,output)[0])
    return acc

def trainAlModel(alSet,nIters,originalIters,alSetDir,modelType):
    setTrainCommand = trainCommandTemplate.format(nIters,alSet,modelType,\
                                                  modelType,originalIters,alSetDir,alSet)
    output = trainModel(setTrainCommand)
    print("trainAlModel")
    print(output)
    return None

def testAlModel(alSet,nIters,modelType):
    setTestCommand = testCommandTemplate.format(modelType,modelType,alSet,nIters)
    return testModel(setTestCommand)

def getAlSubsetInfo(alSet):
    alSetFn = alSet + ".txt"
    alSetPath = osp.join(alSetDir,alSetFn)
    alSubset = getIdList(alSetPath)
    return alSubset

def saveStatePickle(saveALResultsFn,alIds,originalAcc,subsetIndex,cover):
    stateInfo = {"subsetIndex":subsetIndex,"cover":cover}
    saveALResultsFnPkl = saveALResultsFn+".pkl"
    print("saving AL state information to {}".format(saveALResultsFnPkl))
    with open(saveALResultsFnPkl, 'wb') as f:
        pickle.dump({'alIds':alIds,'originalAcc':originalAcc,'stateInfo':stateInfo}, f)

def restoreStatePickle(saveALResultsFn):
    saveALResultsFnPkl = saveALResultsFn+".pkl"
    with open(saveALResultsFnPkl, 'rb') as f:
        stateInfo = pickle.load(f)
    return stateInfo['alIds'],stateInfo['originalAcc'],stateInfo['stateInfo']

def savePickleFile(saveALResultsFn,alIds,originalAcc):
    saveALResultsFnPkl = saveALResultsFn+".pkl"
    print("saving raw AL results to {}".format(saveALResultsFnPkl))
    with open(saveALResultsFnPkl, 'wb') as f:
        pickle.dump({'alIds':alIds,'originalAcc':originalAcc}, f)

def saveCsvFile(saveALResultsFn,alIds,originalAcc):
    saveALResultsFnCsv = saveALResultsFn+".csv"
    print("saving summary stat AL results to {}".format(saveALResultsFnCsv))
    f = open(saveALResultsFnCsv,"w+")
    f.write("image_index,mean_acc,std_acc\n")
    for image_index,acc_list in alIds.items():
        mean = ""
        std = ""
        if acc_list is None:
            pass
            #print("warning: we actually have a none; very unlikely to occur")
        else:
            percent_diff = computePercentDifference(acc_list,originalAcc)
            print("% difference")
            print(percent_diff)
            print(acc_list)
            print(originalAcc)
            mean = np.mean(percent_diff)
            std = np.std(percent_diff)
        f.write("{},{},{}\n".format(image_index,mean,std))
    f.close()

def computePercentDifference(accuracy_list,originalAccuracy):
    acc = np.array(accuracy_list)
    oa = originalAccuracy
    return (acc - oa) / ((acc + oa) / 2.)

def handleTrainedModel(caffemodelPath):
    """
    the caffemodel needs to be copied to the solverstate dir &
    the solverstate needs to be copied to the local dir
    """
    cmDir = '/'.join(caffemodelPath.split("/")[:-1])
    trainModelNetNameBase = caffemodelPath.split("/")[-1].split(".")[0]
    ss = trainModelNetNameBase+".solverstate"
    cm = trainModelNetNameBase+".caffemodel"
    # copySolverstateDest = osp.join(cmDir,ss)
    copyCaffemodelDest = osp.join(cmDir,ss)
    copyCommand = "cp {} {}".format(caffemodelPath,copyCaffemodelDest)
    print(runCommandProcess(copyCommand))
    return ss

if __name__ == "__main__":

    # arguments
    oIters = 6000
    alInfoFn = "/home/gauenk/Documents/data/mnist/ImageSets/AL_single/alSet_info.txt"
    modelType = "lenet5"
    nPassesofAlSubsets = 1
    saveMod = 300
    restoreState = False

    #-----------------------
    # CREATE ORIGINAL MODEL
    #-----------------------

    # train original model
    # caffemodel = trainOriginalModel(oIters,modelType)
    # solverstate = handleTrainedModel(caffemodel)
    caffemodel = "./output/classification/mnist/mnist_al_net_lenet5_iter_6000.caffemodel"
    solverstate = "./mnist_al_net_lenet5_iter_6000.solverstate"

    # test original model
    originalAcc = testOriginalModel(caffemodel,modelType)
    print(originalAcc)

    #-------------------
    # ACTIVE LEARNING
    #-------------------

    # set al arguments from generating the al subsets
    alInfo = getAlInfo(alInfoFn)
    alSetOrigin = alInfo['alSetOrigin']
    alSetDir = alInfo['alSetDir']
    nIters = alInfo['subsetSize'] * nPassesofAlSubsets + oIters

    # get of ALL image indicies available for active learning
    alIds = dict.fromkeys(getIdList(alSetOrigin),None)

    # save active learning information
    saveALResultsFn = "resultsAL_{}_{}_{}_{}".format(nIters,
                                                     alInfo['valSize'],
                                                     alInfo['subsetSize'],
                                                     alInfo['numberOfCovers'])
    # load state information
    start_cover,start_subset = 0,0
    if restoreState:
        alIds,originalAcc,stateInfo = restoreStatePickle(saveALResultsFn)
        start_cover,start_subset = stateInfo['cover'],stateInfo['subsetIndex']

    # run the AL experiments
    for cover in range(start_cover,alInfo['numberOfCovers']):
        for subsetIndex in range(start_subset,alInfo['subsetsInCover']):
            alSet = "alSet_{}_{}_{}_{}".format(subsetIndex,cover,
                                               alInfo['subsetSize'],
                                               alInfo['valSize'])
            trainAlModel(alSet,nIters,oIters,alSetDir,modelType) # train the new model
            acc = testAlModel(alSet,nIters,modelType) # test the new model
            assignAccuracy(alIds,alSet,acc) # aggregate the accuracy
            # save the current state information
            if subsetIndex % saveMod == 0:
                saveStatePickle(saveALResultsFn,alIds,originalAcc,subsetIndex,cover)

    # save information
    savePickleFile(saveALResultsFn,alIds,originalAcc)
    saveCsvFile(saveALResultsFn,alIds,originalAcc)


# -*- coding: utf-8 -*-

from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
import odoo.addons.decimal_precision as dp
from datetime import datetime
from dateutil.relativedelta import relativedelta
import math
import logging
EPS = 0.00001

_logger = logging.getLogger(__name__)
DF = "%Y-%m-%d"

class Deductions(models.Model):
    _inherit = "wc.loan.deduction"

    is_created_by_manual = fields.Boolean('')

    #b589
    @api.onchange('code')
    def on_change_ded_code(self):
        self.ensure_one()

        member_id = self.loan_id.member_id.id
        domain_str="""
           ['|',('account_type','=','%s'),('account_type','=','%s'),'|'
           ,('member_id','=',%s),('other_member_ids','in',%s),('state','=','open')]
           """
        if self.code and self.code.upper() == "SA" and member_id:
            domain_str = domain_str % ('sa','SA',member_id,member_id)
        elif self.code and self.code.upper() == "CBU" and member_id:
            domain_str = domain_str % ('cbu','CBU',member_id,member_id)
        else:
            domain_str = "[('member_id','=',False)]"

        return {'domain':{'deposit_account_id':domain_str}}

    #b563
    @api.constrains('is_created_by_manual','code')
    def validate_for_manual_input(self):
        self.ensure_one()
        if self.is_created_by_manual:
            if self.code.upper() in ['INT','PCP','PEN','ADV-INT']:
                raise UserError(_("Cannot use reserved deduction code [%s].") % (self.code))