from argparse import ArgumentError
import astropy.constants as ac
import astropy.units as au
import numpy as np
from scipy.optimize import brentq

# setting up some convenient conversion factors
_Gconst_cgs = ac.G.cgs.value
_pc_cgs = ac.pc.cgs.value
_kbol_cgs = ac.k_B.cgs.value

_surf_cgs = (ac.M_sun / ac.pc**2).cgs.value
_rho_cgs = (ac.M_sun / ac.pc**3).cgs.value
_sfr_cgs = (ac.M_sun / ac.kpc**2 / au.yr).cgs.value
_kms_cgs = (1 * au.km / au.s).cgs.value

_sigma_eff_models = dict()
_sigma_eff_models["tigress_mid"] = dict(sigma_0=9.8, expo=0.15, sigma_min=5)
_sigma_eff_models["tigress_avg"] = dict(sigma_0=12, expo=0.22, sigma_min=5)

_yield_models = dict()
_yield_models["tigress-classic"] = dict(Y0=10**3.86, expo=-0.212)
_yield_models["tigress-classic-decomp"] = dict(
    Yth0=10**4.45, expo_th=-0.506, Ytrb0=1.5 * 10**2.81, expo_trb=-0.06
)
_yield_models["tigress-ncr-decomp"] = dict(
    Yth0=10**4.7, expo_th=-0.5, Ytrb0=10**3.3, expo_trb=-0.1
)
_yield_models["tigress-ncr-decomp-Z01"] = dict(
    Yth0=10**5.2, expo_th=-0.5, Ytrb0=10**3.3, expo_trb=-0.1
)

def get_weight_gas(Sigma_gas):
    """weight due to gas self-gravity"""
    return 0.5 * np.pi * _Gconst_cgs * Sigma_gas**2

def get_weight_star(Sigma_gas, H_gas, Sigma_star, H_star):
    """weight due to stellar gravity"""
    return np.pi * _Gconst_cgs * Sigma_gas * Sigma_star * H_gas / (H_gas + H_star)

def get_weight_star_gaussian(Sigma_gas, H_gas, Sigma_star, H_star):
    """weight due to stellar gravity for Gaussian profiles"""
    return 2 * _Gconst_cgs * Sigma_gas * Sigma_star * np.arctan(H_gas / H_star)

def get_weight_dm(Sigma_gas, H_gas, Omega_d, zeta_d=1 / 3.0):
    """weight due to dark matter gravity"""
    return zeta_d * Sigma_gas * H_gas * Omega_d**2

def get_pressure(Sigma_gas, H_gas, sigma_eff):
    """total pressure"""
    return 0.5 * Sigma_gas / H_gas * sigma_eff**2

@np.vectorize
def get_weights(
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff, zeta_d=1 / 3.0, method="analytic"
):
    """calculate gas scale height and then each weight term

    Parameters
    ----------
    Sigma_gas: float or array-like
        gas surface density
    Sigma_star: float or array-like
        stellar surface density
    Omega_d: float or array-like
        galactic rotation speed
    H_star: float or array-like
        stellar scale height
    sigma_eff: str or float or array-like
        velocity dispersion
    zeta_d: float [1/3.]
        parameter for dm and gas distribution
    method: str [analytic or numerical]
        calculate scale height either analytically or numerically

    Returns
    -------
    H_gas: float or array-like
        gas scale height
    W_gas: float or array-like
        weight by gas
    W_star: float or array-like
        weight by star
    W_dm: float or array-like
        weight by dark matter

    Example
    -------
    >>> import prfm
    >>> H, W_gas, W_star, W_dm = prfm.get_weights(Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff)
    """
    H_gas = get_scale_height(
        Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff, zeta_d=zeta_d, method=method
    )

    W_gas = get_weight_gas(Sigma_gas)
    W_star = get_weight_star(Sigma_gas, H_gas, Sigma_star, H_star)
    W_dm = get_weight_dm(Sigma_gas, H_gas, Omega_d, zeta_d=zeta_d)

    return H_gas, W_gas, W_star, W_dm

def get_weight_star_thick(Sigma_gas, rho_star, sigma_eff):
    W_star = Sigma_gas * np.sqrt(2 * _Gconst_cgs * rho_star) * sigma_eff
    return W_star

def get_weights_thick(Sigma_gas, rho_star, sigma_eff):
    """calculate gas scale height and then each weight term for H_*>>H_gas

    Parameters
    ----------
    Sigma_gas: float or array-like
        gas surface density
    rho_star: float or array-like
        stellar volume density
    sigma_eff: str or float or array-like
        velocity dispersion

    Returns
    -------
    H_gas: float or array-like
        gas scale height
    W_gas: float or array-like
        weight by gas
    W_star: float or array-like
        weight by star
    W_dm: float or array-like
        weight by dark matter

    Note
    ----
    Used approximate formula Equation (7) in Ostriker and Kim (2022)
    """
    H_gas = get_scale_height_thick(Sigma_gas, rho_star, sigma_eff)

    W_gas = get_weight_gas(Sigma_gas)
    W_star = get_weight_star_thick(Sigma_gas, rho_star, sigma_eff)
    W_dm = 0.0 * H_gas / H_gas

    return H_gas, W_gas, W_star, W_dm

def get_weight_star_thin(Sigma_gas, Sigma_star, sigma_eff):
    W_star = np.pi * _Gconst_cgs * Sigma_gas * Sigma_star
    return W_star

def get_weights_thin(Sigma_gas, Sigma_star, sigma_eff):
    """calculate gas scale height and then each weight term for H_*>>H_gas

    Parameters
    ----------
    Sigma_gas: float or array-like
        gas surface density
    Sigma_star: float or array-like
        stellar surface density
    sigma_eff: str or float or array-like
        velocity dispersion

    Returns
    -------
    H_gas: float or array-like
        gas scale height
    W_gas: float or array-like
        weight by gas
    W_star: float or array-like
        weight by star
    W_dm: float or array-like
        weight by dark matter

    Note
    ----
    Used approximate formula Equation (7) in Ostriker and Kim (2022)
    """
    H_gas = get_scale_height_thin(Sigma_gas, Sigma_star, sigma_eff)

    W_gas = get_weight_gas(Sigma_gas)
    W_star = get_weight_star_thin(Sigma_gas, Sigma_star, sigma_eff)
    W_dm = 0.0 * H_gas / H_gas

    return H_gas, W_gas, W_star, W_dm

@np.vectorize
def get_weight_contribution(
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff, zeta_d=1 / 3.0, method="analytic"
):
    """Wrapper function to calculate the ratio of each weight term to the total weight

    Parameters
    ----------
    Sigma_gas: float or array-like
        gas surface density
    Sigma_star: float or array-like
        stellar surface density
    Omega_d: float or array-like
        galactic rotation speed
    H_star: float or array-like
        stellar scale height
    sigma_eff: str or float or array-like
        velocity dispersion
    zeta_d: float [1/3.]
        parameter for dm and gas distribution
    method: str [analytic or numerical]
        calculate scale height either analytically or numerically

    Returns
    -------
    f_gas: float or array-like
        weight by gas/total weight
    f_star: float or array-like
        weight by star/total weight
    f_dm: float or array-like
        weight by dark matter/total weight
    """
    H, wgas, wstar, wdm = get_weights(
        Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff, zeta_d=zeta_d, method=method
    )
    wtot = wgas + wstar + wdm

    return wgas / wtot, wstar / wtot, wdm / wtot

@np.vectorize
def get_scale_height_gas_only(*args, **kwargs):
    """analytic solution for gas only case"""
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff = args
    return sigma_eff**2 / (np.pi * _Gconst_cgs * Sigma_gas)

@np.vectorize
def get_scale_height_star_only(*args, **kwargs):
    """analytic solution for star only case"""
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff = args
    h = sigma_eff**2 / (4 * np.pi * _Gconst_cgs * Sigma_star)
    h_star = H_star / h
    return h * (1 + np.sqrt(1 + 2 * h_star))

@np.vectorize
def get_scale_height_dm_only(*args, **kwargs):
    """analytic solution for dark matter only case"""
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff = args
    zeta_d = kwargs["zeta_d"]
    return sigma_eff / np.sqrt(2 * zeta_d) / Omega_d

@np.vectorize
def get_scale_height_star_gas(*args, **kwargs):
    """analytic solution neglecting dark matter"""
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff = args
    H_gas_only = get_scale_height_gas_only(*args, **kwargs)
    eta_star = H_star / H_gas_only
    s_star = Sigma_star / Sigma_gas
    # h = ((1-eta_star)+np.sqrt((eta_star-1)**2+4*eta_star*(1+2.*s_star)))/(2*(1+2*s_star))
    h = (
        2
        * eta_star
        / ((eta_star - 1) + np.sqrt((eta_star + 1) ** 2 + 8 * s_star * eta_star))
    )
    return h * H_gas_only

@np.vectorize
def get_scale_height_star_gas_approx(*args, **kwargs):
    """analytic solution neglecting dark matter"""
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff = args
    H_gas_only = get_scale_height_gas_only(*args, **kwargs)
    eta_star = H_star / H_gas_only
    s_star = Sigma_star / Sigma_gas
    h = (1 + np.sqrt(1 + 8 * s_star * eta_star)) / (4 * s_star)
    return h * H_gas_only

@np.vectorize
def get_scale_height_dm_gas(*args, **kwargs):
    """analytic solution neglectic star"""
    H_gas_only = get_scale_height_gas_only(*args, **kwargs)
    H_dm_only = get_scale_height_dm_only(*args, **kwargs)
    eta_D = H_dm_only / H_gas_only
    h = (-(eta_D**2) + np.sqrt(eta_D**4 + 4 * eta_D**2)) * 0.5
    return h * H_gas_only

@np.vectorize
def get_sigma_eff(P, model="tigress_mid"):
    """pressure-dependent velocity dispersion model"""
    sigma_eff_model = _sigma_eff_models[model]
    sigma_0 = sigma_eff_model["sigma_0"]
    expo = sigma_eff_model["expo"]
    sigma_min = sigma_eff_model["sigma_min"]

    veld = sigma_0 * (1.0e-4 * P / _kbol_cgs) ** expo

    return np.clip(veld, sigma_min, None) * 1.0e5

@np.vectorize
def get_feedback_yield(P, model="tigress-classic"):
    """total feedback yield as a function of weight

    Paramerters
    -----------
    P : float
        midplane pressure or weight
    model : str
        tigress-classic for OK22
        tigress-NCR for K23

    Returns
    -------
    Ytot : float
        feedback yield in km/s
    """
    yield_model = _yield_models[model]
    if "Y0" in yield_model:
        Y0 = yield_model["Y0"]
        slope = yield_model["expo"]
        Ytot = Y0 * (P / _kbol_cgs) ** slope
    elif "Yth0" in yield_model:
        Yth0 = yield_model["Yth0"]
        expo_th = yield_model["expo_th"]
        Ytrb0 = yield_model["Ytrb0"]
        expo_trb = yield_model["expo_trb"]
        Ytot = Yth0 * (P / _kbol_cgs) ** expo_th + Ytrb0 * (P / _kbol_cgs) ** expo_trb
    return Ytot

@np.vectorize
def get_feedback_yield_comp(P, comp="th", model="tigress-classic-decomp"):
    """feedback yield of each component as a function of weight

    Paramerters
    -----------
    P : float
        midplane pressure or weight
    comp : str
        component name ['th','trb']
    model : str
        tigress-classic for OK22
        tigress-NCR for K23

    Returns
    -------
    Y : float
        feedback yield in km/s
    """
    yield_model = _yield_models[model]
    Y0 = yield_model["Y{}0".format(comp)]
    expo = yield_model["expo_{}".format(comp)]
    return Y0 * (P / _kbol_cgs) ** expo

def get_sfr(P, Ytot="tigress-classic"):
    """calculate SFR surface density using feedback yield

    Paramerters
    -----------
    P : float
        midplane pressure or weight
    Ytot : float, str
        constant value in km/s or
        P-dependent feedback yield model e.g., `tigress-classic`

    Returns
    -------
    sfr : float
        SFR surface density in g/cm^2/yr
    """
    Ytot = get_feedback_yield(P, model=Ytot) if isinstance(Ytot, str) else Ytot
    return P / (Ytot * 1.0e5)

def get_scale_height(
    Sigma_gas,
    Sigma_star,
    Omega_d,
    H_star,
    sigma_eff,
    zeta_d=1 / 3.0,
    method="analytic",
    wgas=1,
    wstar=1,
    wdm=1,
):
    """wrapper function to calculate gas scale height either numerically or analytically

    Parameters
    ----------
    Sigma_gas : float or array-like
        gas surface density
    Sigma_star : float or array-like
        stellar surface density
    Omega_d : float or array-like
        galactic rotation speed
    H_star : float or array-like
        stellar scale height
    sigma_eff : str or float or array-like
        velocity dispersion
    zeta_d : float [1/3.]
        parameter for dm and gas distribution
    method : str [analytic or numerical]
        calculate scale height either analytically or numerically

    wgas : int [0 or 1]
        toggle weight term from gas
    wstar : int [0 or 1]
        toggle weight term from stars
    wdm : int [0 or 1]
        toggle weight term from dark matter

    Returns
    -------
    H : float or array-like
        scale height in cm

    Example
    -------
    >>> import prfm
    >>> H=prfm.get_scale_height(Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff)
    """
    args = (Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff)
    kwargs = dict(zeta_d=zeta_d, wgas=wgas, wstar=wstar, wdm=wdm)
    w = (wgas << 2) + (wstar << 1) + (wdm << 0)

    if method == "numerical":
        return get_scale_height_numerical(*args, **kwargs)
    else:
        if w == 7:  # 111
            return get_scale_height_analytic(*args, zeta_d=zeta_d)
        elif w == 6:  # 110
            return get_scale_height_star_gas(*args, zeta_d=zeta_d)
        elif w == 5:  # 101
            return get_scale_height_dm_gas(*args, zeta_d=zeta_d)
        elif w == 4:  # 100
            return get_scale_height_gas_only(*args, zeta_d=zeta_d)
        elif w == 3:  # 011
            # no analytic solution provided
            return
        elif w == 2:  # 010
            return get_scale_height_star_only(*args, zeta_d=zeta_d)
        elif w == 1:  # 001
            return get_scale_height_dm_only(*args, zeta_d=zeta_d)
        else:
            # no such gas is expected
            raise ("at least one term must be considered")

def get_scale_height_thick(Sigma_gas, rho_star, sigma_eff, zeta_d=1 / np.pi):
    """approximate solution for H_* >> H_gas without explicitly defining H_*

    Parameters
    ----------
    Sigma_gas : float or array-like
        gas surface density
    rho_star : float or array-like
        stellar(+dark matter) volume density
    sigma_eff : str or float or array-like
        velocity dispersion

    Returns
    -------
    H : float or array-like
        scale height in cm

    Notes
    -----
    Ostriker & Kim (2022) Equation (5)
    """
    g1 = np.pi * _Gconst_cgs * Sigma_gas
    g2 = 32 * np.pi * zeta_d * _Gconst_cgs * rho_star * sigma_eff**2
    return 2 * sigma_eff**2 / (g1 + np.sqrt(g1**2 + g2))

def get_scale_height_thin(Sigma_gas, Sigma_star, sigma_eff):
    """approximate solution for H_* << H_gas without explicitly defining H_*

    Parameters
    ----------
    Sigma_gas : float or array-like
        gas surface density
    Sigma_star : float or array-like
        stellar surface density
    sigma_eff : str or float or array-like
        velocity dispersion

    Returns
    -------
    H : float or array-like
        scale height in cm

    Notes
    -----
    Ostriker & Kim (2022) Equation (5)
    """
    g1 = np.pi * _Gconst_cgs * Sigma_gas
    g2 = 2 * np.pi * _Gconst_cgs * Sigma_star
    return sigma_eff**2 / (g1 + g2)

@np.vectorize
def get_scale_height_analytic(
    Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff, zeta_d=1 / 3.0
):
    """Analytic solution of the cubic equation for the vertical dynamical equilibirum
    including all three weight terms.

    All inputs must be in c.g.s. units
    """
    args = (Sigma_gas, Sigma_star, Omega_d, H_star, sigma_eff)
    kwargs = dict(zeta_d=zeta_d)
    H_gas_only = get_scale_height_gas_only(*args, **kwargs)
    H_dm_only = get_scale_height_dm_only(*args, **kwargs)

    s_star = Sigma_star / Sigma_gas
    eta_star = H_star / H_gas_only
    eta_dm_sq = (H_dm_only / H_gas_only) ** 2

    a0 = -eta_star * eta_dm_sq
    a1 = (eta_star - 1) * eta_dm_sq
    a2 = (1 + 2 * s_star) * eta_dm_sq + eta_star

    Q = (3 * a1 - a2**2) / 9.0
    R = (9 * a2 * a1 - 27 * a0 - 2 * a2**3) / 54.0

    theta = np.arccos(R / np.sqrt(-(Q**3)))
    h = 2 * np.sqrt(-Q) * np.cos(theta / 3) - a2 / 3.0

    return h * H_gas_only

@np.vectorize
def get_scale_height_numerical(
    Sigma_gas,
    Sigma_star,
    Omega_d,
    H_star,
    sigma_eff,
    zeta_d=1 / 3.0,
    wgas=1,
    wstar=1,
    wdm=1,
    fgas=get_weight_gas,
    fstar=get_weight_star,
    fdm=get_weight_dm,
):
    """Numerical solution of the vertical dynamical equilibrium equation
    including all weight terms.

    All inputs must be in c.g.s. units

    Parameters
    ----------
    fgas : func
    fstar : func
    fdm : func
    """

    def fun(x):
        return (
            get_pressure(Sigma_gas, x, sigma_eff)
            - wgas * fgas(Sigma_gas)
            - wstar * fstar(Sigma_gas, x, Sigma_star, H_star)
            - wdm * fdm(Sigma_gas, x, Omega_d, zeta_d=zeta_d)
        )

    # soln=root(fun,H_gas_init)
    # return soln.x
    return brentq(fun, 1.0e-3 * _pc_cgs, 1.0e6 * _pc_cgs)

def get_self_consistent_solution(
    Sigma_gas,
    Sigma_star,
    Omega_d,
    H_star,
    sigma_eff="tigress_mid",
    zeta_d=1 / 3.0,
    method="analytic",
    tol=1.0e-5,
    niter=16,
):
    """Iteratively solve for sigma_eff(P) to get converged weight, H, and sigma_eff

    Parameters
    ----------
    Sigma_gas: float or array-like
        gas surface density
    Sigma_star: float or array-like
        stellar surface density
    Omega_d: float or array-like
        galactic rotation speed
    H_star: float or array-like
        stellar scale height
    sigma_eff : float or str
        effective vertical velocity dispersion
    zeta_d: float [1/3.]
        parameter for dm and gas distribution
    method: str [analytic or numerical]
        calculate scale height either analytically or numerically

    Returns
    -------
    Wtot : float or array
    H : float or array
    sigma_eff: float or array
    """
    # make initial guess
    if not isinstance(sigma_eff, str):
        sigma0 = sigma_eff
    else:
        if not (sigma_eff in _sigma_eff_models):
            raise IndexError("sigma_eff models: ", list(_sigma_eff_models.keys()))
        sigma0 = 15.0e5
    args = (Sigma_gas, Sigma_star, Omega_d, H_star, sigma0)
    kwargs = dict(zeta_d=zeta_d, method=method)
    Hprev, wgas, wstar, wdm = get_weights(*args, **kwargs)
    wtot_prev = wgas + wstar + wdm

    # return if velocity diseprsion is a constant
    if not isinstance(sigma_eff, str):
        return wtot_prev, Hprev, sigma0 * np.ones_like(Hprev)

    # iterative solve
    for i in range(niter):
        new_sigma_eff = get_sigma_eff(wtot_prev, model=sigma_eff)
        args = (Sigma_gas, Sigma_star, Omega_d, H_star, new_sigma_eff)
        Hnext, wgas, wstar, wdm = get_weights(*args, **kwargs)
        wtot_next = wgas + wstar + wdm
        # L1_norm = np.sum(np.abs(wtot_next/wtot_prev - 1))
        L1_norm = np.sum(np.abs(Hnext / Hprev - 1))
        if L1_norm < tol:
            break
        wtot_prev = np.copy(wtot_next)
        Hprev = np.copy(Hnext)

    return wtot_next, Hnext, get_sigma_eff(wtot_next, model=sigma_eff)

class PRFM(object):
    """a wrapper class for scale height, weight, SFR calculations

    Parameters
    ----------
    Sigma_gas : float, array-like
        gas surface density
    Sigma_star : float, array-like
        stellar surface density
    Omega_d : float, array-like
        galactic rotation speed
    H_star : float, array-like
        stellar scale height
    sigma_eff : str, float, array-like
        velocity dispersion
    zeta_d : float [1/3.]
        parameter for dm and gas distribution
    method : str [analytic or numerical]
        calculate scale height either analytically or numerically
    """

    def __init__(
        self,
        Sigma_gas=None,
        Sigma_star=None,
        H_star=None,
        rho_star=None,
        Omega_d=None,
        rho_dm=None,
        sigma_eff="tigress-mid",
        astro_units=True,
    ):
        # initialize method attribute
        self._method = "analytic"

        # set model name based on sigma_eff
        self.name = sigma_eff if isinstance(sigma_eff, str) else "constant"

        # initialize unit conversion factors
        self.units = self._set_units()

        # setting up gas surface density
        if Sigma_gas is None:
            # gas density must be given
            raise ArgumentError("Sigma_gas must be provided")
        self._Sigma_gas = Sigma_gas

        # setting up stellar disk
        if (Sigma_star is None) and (rho_star is None):
            self._Sigma_star = 0
            self._rho_star = 0
            self._wstar = 0
        else:
            self._wstar = 1
            if H_star is None:
                if rho_star is None:
                    # this is limit for H_star << H_gas
                    self._stellar_disk = "thick"
                    self._Sigma_star = Sigma_star
                elif Sigma_star is None:
                    # this is limit for H_star >> H_gas
                    self._stellar_disk = "thick"
                    self._rho_star = rho_star
                else:
                    # both given
                    self._stellar_disk = "general"
                    self._H_star = Sigma_star / (2 * rho_star)
            else:
                if rho_star is None:
                    # Sigma_star is given
                    rho_star = Sigma_star / (2 * H_star)
                elif Sigma_star is None:
                    # rho_star is given
                    Sigma_star = rho_star * 2 * H_star
                else:
                    # both given
                    raise ArgumentError(
                        "cannot provide all three: Sigma_star, rho_star, H_star"
                    )

                self._stellar_disk = "general"
                self._Sigma_star = Sigma_star
                self._rho_star = rho_star
                self._H_star = H_star
        # unit conversion done before DM part
        if astro_units:
            self._astro_to_cgs()

        # setting up dark matter
        if (Omega_d is None) and (rho_dm is None):
            self._Omega_d = 0
            self._rho_dm = 0
            self._wdm = 0
        else:
            self._wdm = 1
            # this conversion assumes flat rotation
            _fourpiG = 4 * np.pi * ac.G.cgs.value
            if rho_dm is None:
                # Omega_d is given
                if astro_units:
                    Omega_d = Omega_d * self.units["Omega_d"].cgs.value
                rho_dm = Omega_d**2 / _fourpiG
            elif Omega_d is None:
                # rho_dm is given
                if astro_units:
                    rho_dm = rho_dm * self.units["rho_dm"].cgs.value
                Omega_d = np.sqrt(_fourpiG * rho_dm)
            else:
                # both given
                raise ArgumentError("cannot provide both Omega_d and rho_dm")

            self._Omega_d = Omega_d
            self._rho_dm = rho_dm

        if not isinstance(sigma_eff, str):
            if astro_units:
                u = self.units["sigma_eff"].cgs.value
                sigma_eff = sigma_eff * u
            self._sigma_eff = sigma_eff
        else:
            self._sigma_eff_model = sigma_eff

        # set arguments that will be passed to the functions
        if self._stellar_disk == "general":
            self._args = (
                self._Sigma_gas,
                self._Sigma_star,
                self._Omega_d,
                self._H_star,
                sigma_eff,
            )
        elif self._stellar_disk == "thick":
            self._args = (self._Sigma_gas, self._rho_star, sigma_eff)
        elif self._stellar_disk == "thin":
            self._args = (self._Sigma_gas, self._Sigma_star, sigma_eff)
        # store parameters in astro-friendly units
        self._cgs_to_astro()

        # set weight functions
        self._weight_functions = [get_weight_gas, get_weight_star, get_weight_dm]

    def set_weight_function_gas(self, func):
        """Update weight function for gas"""
        self._weight_functions[0] = func
        self._method = "numerical"

    def set_weight_function_star(self, func):
        """Update weight function for star"""
        self._weight_functions[1] = func
        self._method = "numerical"

    def set_weight_function_dm(self, func):
        """Update weight function for star"""
        self._weight_functions[2] = func
        self._method = "numerical"

    def reset_arg_list(self, sigma_eff):
        # reset arguments with new velocity dispersion
        if self._stellar_disk == "general":
            self._args = (
                self._Sigma_gas,
                self._Sigma_star,
                self._Omega_d,
                self._H_star,
                sigma_eff,
            )
        elif self._stellar_disk == "thick":
            self._args = (self._Sigma_gas, self._rho_star, sigma_eff)
        elif self._stellar_disk == "thin":
            self._args = (self._Sigma_gas, self._Sigma_star, sigma_eff)

    def _astro_to_cgs(self):
        for var in [
            "Sigma_gas",
            "Sigma_star",
            "H_star",
            "rho_star",
            "rho_dm",
            "Omega_d",
            "sigma_eff",
        ]:
            if hasattr(self, "_" + var):
                u_cgs = self.units[var].cgs.value
                v = getattr(self, "_" + var) * u_cgs
                setattr(self, "_" + var, v)

    def _cgs_to_astro(self):
        for var in [
            "Sigma_gas",
            "Sigma_star",
            "H_star",
            "rho_star",
            "rho_dm",
            "Omega_d",
            "sigma_eff",
        ]:
            if hasattr(self, "_" + var):
                u_cgs = self.units[var].cgs.value
                v = getattr(self, "_" + var) / u_cgs
                setattr(self, var, v)

    def _set_units(self):
        units = dict()
        units["Sigma_gas"] = units["Sigma_star"] = ac.M_sun / ac.pc**2
        units["rho_star"] = units["rho_dm"] = ac.M_sun / ac.pc**3
        units["Omega_d"] = au.km / au.s / ac.kpc
        units["H_star"] = units["H_gas"] = ac.pc
        units["Wtot"] = ac.k_B / au.cm**3 * au.K
        units["Wgas"] = units["Wstar"] = units["Wdm"] = units["Wtot"]
        units["Ytot"] = 1.0 * au.km / au.s
        units["sigma_eff"] = 1.0 * au.km / au.s
        units["Sigma_SFR"] = ac.M_sun / ac.kpc**2 / au.yr

        return units

    def __repr__(self) -> str:
        args = "PRFM calculator is prepared for\n"
        for var in [
            "Sigma_gas",
            "Sigma_star",
            "rho_star",
            "Omega_d",
            "rho_dm",
            "sigma_eff",
        ]:
            if not hasattr(self, "_" + var):
                continue
            v = np.atleast_1d(getattr(self, var))
            args += "  {}[0]: {}, N={}\n".format(var, v[0], len(v))
        if hasattr(self, "_sigma_eff_model"):
            args += "  sigma_eff_model: {}\n".format(self._sigma_eff_model)
        return args

    def get_scale_height(self, method=None, wgas=1, wstar=1, wdm=1):
        """Wrapper function to get the gas scale height

        Parameters
        ----------
        method : str
            override method if provided
        """

        if method is None:
            method = self._method

        if self._stellar_disk == "general":
            H_gas = get_scale_height(*self._args, wgas=wgas, wstar=wstar, wdm=wdm)
        elif self._stellar_disk == "thick":
            H_gas = get_scale_height_thick(*self._args)
        elif self._stellar_disk == "thin":
            H_gas = get_scale_height_thin(*self._args)

        return H_gas / self.units["H_gas"].cgs.value

    def get_scale_height_numerical(self):
        """Wrapper function to get the gas scale height for generic weight functions"""

        H_gas = get_scale_height_numerical(
            *self._args,
            fgas=self._weight_functions[0],
            fstar=self._weight_functions[1],
            fdm=self._weight_functions[2]
        )
        return H_gas / self.units["H_gas"].cgs.value

    def get_weight_contribution(self):
        """Wrapper function to get weight contribution"""
        if not hasattr(self, "Wtot"):
            self.calc_weights()
        return self.Wgas / self.Wtot, self.Wstar / self.Wtot, self.Wdm / self.Wtot

    def calc_weights(self, method=None):
        """Wrapper function to calculate scale height and all weights

        Parameters
        ----------
        method : str
            override method if provided
        """

        if method is None:
            method = self._method

        if method == "analytic":
            H_gas = self.get_scale_height(wstar=self._wstar, wdm=self._wdm)
        elif method == "numerical":
            H_gas = self.get_scale_height_numerical()

        _H_gas = H_gas * self.units["H_gas"].cgs.value

        self.H_gas = H_gas
        self._H_gas = _H_gas

        W_gas = self._weight_functions[0](self._Sigma_gas)
        if self._stellar_disk == "thick":
            W_star = get_weight_star_thick(*self._args)
        elif self._stellar_disk == "thin":
            W_star = get_weight_star_thin(*self._args)
        else:
            W_star = self._weight_functions[1](
                self._Sigma_gas, _H_gas, self._Sigma_star, self._H_star
            )
        W_dm = self._weight_functions[2](self._Sigma_gas, _H_gas, self._Omega_d)

        for var, v in zip(
            ["H_gas", "Wgas", "Wstar", "Wdm"], [_H_gas, W_gas, W_star, W_dm]
        ):
            u_cgs = self.units[var].cgs.value
            setattr(self, "_" + var, v)  # results in cgs
            setattr(self, var, v / u_cgs)  # results in astro units

        # total weight
        Wtot = W_gas + W_star + W_dm
        u_cgs = self.units["Wtot"].cgs.value
        self._Wtot = Wtot
        self.Wtot = Wtot / u_cgs

        if hasattr(self, "_sigma_eff_model"):
            self._sigma_eff = get_sigma_eff(Wtot, model=self._sigma_eff_model)
            self.sigma_eff = self._sigma_eff / self.units["sigma_eff"].cgs.value

    def calc_self_consistent_solution(self, method=None, niter=16, tol=1.0e-6):
        """Wrapper function to calculate self-consistent solutions

        Update H_gas, W_tot (and all components), sigma_eff
        """
        if method is None:
            method = self._method

        # for model sigma, need to give an initial guess
        if hasattr(self, "_sigma_eff_model"):
            self.reset_arg_list(15.0e5)
        self.calc_weights(method=method)

        # that's it for constant sigma_eff
        # iterative solve for model sigma
        if hasattr(self, "_sigma_eff_model"):
            # iterative solve
            for i in range(niter):
                Hprev = np.copy(self._H_gas)
                self.reset_arg_list(self._sigma_eff)
                self.calc_weights(method=method)
                L1_norm = np.sum(np.abs(self._H_gas / Hprev - 1))
                if L1_norm < tol:
                    break

    def calc_sfr(self, Ytot=1.0e3):
        """Wrapper function to calculate SFR surface density

        Update Sigma_SFR (astro units) and _Sigma_SFR (cgs)
        """
        if not hasattr(self, "_Wtot"):
            self.calc_self_consistent_solution()

        # results in c.g.s.
        Sigma_SFR = get_sfr(self._Wtot, Ytot=Ytot)
        u_cgs = self.units["Sigma_SFR"].cgs.value
        self._Sigma_SFR = Sigma_SFR
        self.Sigma_SFR = Sigma_SFR / u_cgs

    def check_solutions(self):
        self.calc_self_consistent_solution(method="analytic")
        sola = np.copy(self.Wtot)
        self.calc_self_consistent_solution(method="numerical")
        soln = np.copy(self.Wtot)

        L1 = np.mean(np.abs(sola - soln))
        print("difference between analytic and numerical solutions: {}".format(L1))
        return sola, soln


import cv2
import numpy as np
import uuid

im = cv2.imread("test.jpg")
print(im.shape)
print(im[0,0])
print(im[0,0][0])
print(im[0,0][1])
print(im[0,0][2])

x1 = None
y1 = None
x2 = None
y2 = None
backgroundBgr = [255, 255, 255]

for y in range(0, im.shape[0] - 1):
	#print(y)
	for x in range(0, im.shape[1] - 1):
		#print(x)
		bgr = im[y, x]
		for i,v in enumerate(bgr):
			if abs(v - backgroundBgr[i]) > 10:
				if (x1 is None) or (x < x1):
					x1 = x
				if (x2 is None) or (x > x2):
					x2 = x
				if (y1 is None) or (y < y1):
					y1 = y
				if (y2 is None) or (y > y2):
					y2 = y
				break;

print(x1, y1, x2, y2)

cv2.rectangle(im, (x1, y1), (x2, y2), (0,0,255), 3);
cv2.imwrite(str(uuid.uuid4()) + ".jpg", im)

# bgr
'''
cv2.rectangle(im, (10, 10), (20, 20), (0,0,255));

im2 = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

cv2.imshow("Over the Clouds", im)
cv2.imshow("Over the Clouds - gray", im2)

cv2.waitKey(0)
cv2.destroyAllWindows()
'''


from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('web', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='BotSettings',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('training_data_file', models.FileField(upload_to='')),
                ('intents_json_file', models.FileField(upload_to='')),
            ],
            options={
                'abstract': False,
            },
        ),
    ]


import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
import json
from scrapy.utils.project import get_project_settings
import scrapy
def firebase_store(data:list):
    db = firestore.client()
    db.collection('examdetails').document('ntse').collection('scrapy-data').document('imporatant-event').set({
            'data': data
        })
class NtseSpider(scrapy.Spider):
    name = 'ntse'
    start_urls = [
            'https://sslc.karnataka.gov.in/new-page/NTSE-NMMS/en'
            ]
    def parse(self, response):
        # links = response.css('a').xpath('@href').getall()
        heading = response.xpath('//th[2]/text()').getall()
        i=1
        j=-1
        data = []
        while i<(16):
            x = response.xpath('//th[2]/text()')[i].get()
            if (x == '\r\n'):
                continue
            j+=1
            dataitem = {
                "activity" : response.xpath('//h4/span/a/text()')[j].get(),
                'date' : x
            }
            data.append(dataitem)
            i+=1
        firebase_store(data)
#settings = get_project_settings()
#process = CrawlerProcess(settings)
#process.crawl(Spider)
#process.start()


#Write a decorator that will show the hour, minute and second before calling the code of the decorated
# function and after calling the code of the decorated function.

from datetime import datetime

def before_after_calling(func):
    def wrapper(*args, **kwargs):
        print(f"Timpul intial este: {datetime.now().strftime('%H:%M:%S')}.")
        func(*args, **kwargs)
        print(f"Timpul final este: {datetime.now().strftime('%H:%M:%S')}")

    return wrapper

@before_after_calling
def sum_total(salar):
    a = 0
    for i in range (12870000):
        a = salar*salar*salar
    print(f" Salariul anual este de: {a}.")

if __name__== "__main__":
    sum_total(1500)


import re
import requests
from urllib.parse import urlsplit,urlparse
from collections import deque
from bs4 import BeautifulSoup
import pandas as pd
from tqdm import tqdm

# http_proxy  =  "http://192.168.43.164:8080"
# proxyDict = {
              # "http"  : http_proxy,
              # "https" : http_proxy,
            # }

#Your CSV File which must have a column with Title 'Website' and having valid website urls
file = pd.read_csv("physio.csv")

def GetEmailId(url):

    unscraped = deque([url])
    scraped = set()

    #Main Loop
    while len(unscraped):
        url = unscraped.popleft()
        scraped.add(url)

        #Splitting the URL for further use
        parts = urlsplit(url)
        base_url = f"{parts.scheme}://{parts.netloc}"
        if '/' in parts.path:
            path = url[:url.rfind('/')+1]
        else:
            path = url

        print("Crawling URL %s" % url)

        #Getting the WebPages
        try:
            response = requests.get(url)
        except Exception as e:
            print(e)
            continue

        #Getting Email Ids From the HTML File

        new_emails = set(re.findall(r"[a-z0-9\.\-+_]+@[a-z0-9\.\-+_]+\.[a-z0-9\.\-+_]+", response.text, re.I))

        #Only Saves a single Email ID
        if len(new_emails):
            return list(new_emails)[0]

        #Getting any URLs in the Website
        #Its rare that you will file the main email id in the index page, hence the script the crawn the whole website

        soup = BeautifulSoup(response.text, 'lxml')

        for anchor in soup.find_all("a"):
            if "href" in anchor.attrs:
                link = anchor.attrs["href"]
            else:
                link = ''

            if link.startswith('/'):
                link = base_url + link

            elif not link.startswith('http'):
                link = path + link

            if not link in unscraped and not link in scraped:
                unscraped.append(link)

EmailIDs = []
for url in tqdm(file['Website']):
    # Remove Empty and Error prone URLs
    try:
        requests.get(url)
        email = GetEmailId(url)
        print(f'URL - {url} : Email - {email}')
        EmailIDs.append(email)
    except:
        print("Url Not found or Malformed")
        EmailIDs.append("None")

file["Email IDs"] = EmailIDs
file.to_csv('FinalListWithEmails.csv', index=False)


"""
Perform model inference.
"""

import logging
import pandas as pd
import pickle
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import FunctionTransformer
from typing import Union

from birdwatcher.config import DATA_PREP_CONFIG, PATHS
from birdwatcher.dataprep.prep import (
    _generate_raw_proc_path,
    get_dataprep_pipeline
)
from birdwatcher.ml.feature_generation import get_feature_generation_pipeline
from birdwatcher.s3_utils import s3_load_pickle

_logger = logging.getLogger(__name__)
_logger.setLevel(logging.INFO)
_formatter = logging.Formatter(
    "%(asctime)s:%(levelname)s:%(module)s: %(message)s"
)
_console_handler = logging.StreamHandler()
_console_handler.setFormatter(_formatter)
_logger.addHandler(_console_handler)

with open(PATHS.dc_run_date_info, "rb") as infile:
    run_info = pickle.load(infile)

def _generate_inference_dataprep_kwargs(
    data_key: str,
    end_date_name: str = run_info.end_date_name,
    restrict_to_english: bool = False,
    save: bool = True
):
    """
    Generate kwargs for dataprep inference pipeline.
    """
    config_dict = DATA_PREP_CONFIG.processing_info[data_key]
    kwargs = {
        "data_key": data_key,
        "restrict_to_english": restrict_to_english,
        "text_col": config_dict["text_col"]
    }
    if save:
        if data_key == "sentiment140":
            kwargs["save_path"] = (
                DATA_PREP_CONFIG.processing_info["sentiment140"]["path_proc"]
            )
        else:
            df_proc_save_path = _generate_raw_proc_path(
                config_dict=config_dict,
                df_type="proc",
                end_date_name=end_date_name
            )
            kwargs["save_path"] = df_proc_save_path
    return kwargs

def _generate_inference_tfidf_kwargs(
    data_key: str,
    save: bool = True
):
    """
    Generate kwargs for tfidf inference pipeline.
    """
    config_dict = DATA_PREP_CONFIG.processing_info[data_key]
    kwargs = {
        "data_key": data_key,
        "text_col_proc": config_dict["text_col"] + "_processed",
        "save": save
    }
    return kwargs

def _remove_cols(
    df: pd.DataFrame,
    labels: Union[str, list[str]]
) -> pd.DataFrame:
    """
    Remove col from dataframe
    """
    return df.drop(labels=labels, axis=1)

def _get_scores_save_path(
    data_key: str,
    end_date_name: str = run_info.end_date_name
):
    """
    Get save path for scores.
    """
    fname = "scores_" + data_key + f"_{end_date_name}.parquet"
    return PATHS.scores_path + f"/{data_key}/{fname}"

def get_trained_inference_pipeline(
    data_key: str,
    end_date_name: str = run_info.end_date_name,
    restrict_to_english: bool = False,
    save: bool = True,
    verbose: bool = True
):
    """
    Load trained inference pipeline.
    """
    # Generate dataprep and tfidf kwarg & corresponding pipelines.
    _logger.info(
        "Getting dataprep and feature generation pipelines."
    )
    INFERENCE_DATAPREP_KWARGS = _generate_inference_dataprep_kwargs(
        data_key=data_key,
        end_date_name=end_date_name,
        restrict_to_english=restrict_to_english,
        save=save
    )
    INFERENCE_TFIDF_KWARGS = _generate_inference_tfidf_kwargs(
        data_key=data_key,
        save=save
    )
    dataprep_pipeline = get_dataprep_pipeline(
        dataprep_kwargs=INFERENCE_DATAPREP_KWARGS
    )
    feature_generation_pipeline = get_feature_generation_pipeline(
        tfidf_kwargs=INFERENCE_TFIDF_KWARGS
    )

    # Load trained pca & model.
    _logger.info(
        f"Loading trained pca from {PATHS.pca_path}."
    )
    pca = s3_load_pickle(
        key=PATHS.s3_ml_path,
        file_name=PATHS.pca_path
    )
    _logger.info(
        f"Loading trained model from {PATHS.model_path}."
    )
    model = s3_load_pickle(
        key=PATHS.s3_ml_path,
        file_name=PATHS.model_path
    )

    # Combined components into inference pipeline.
    _logger.info(
        "Combining dataprep, feature generation, pca and model "
        "into an inference pipeline."
    )
    steps=[
        ("dataprep", dataprep_pipeline),
        ("feature_generation", feature_generation_pipeline),
        ("pca", pca),
        ("classifier", model)
    ]
    if data_key == "sentiment140":
        _logger.info(
            "Inserting 'droptarget' step to pipeline."
        )
        droptarget = FunctionTransformer(
            _remove_cols,
            kw_args={"labels": "sentiment140_target"},
            validate=False
        )
        steps.insert(2, ("droptarget", droptarget))
    inference_pipeline = Pipeline(
        steps=steps,
        verbose=verbose
    )
    return inference_pipeline

def perform_pipeline_inference(
    data_key: str,
    end_date_name: str = run_info.end_date_name,
    save: bool = True,
    verbose: bool = True
):
    """
    Retreive pipeline and perform inference.
    """
    # Retreive trained inference pipeline.
    inference_pipeline = get_trained_inference_pipeline(
        data_key=data_key,
        end_date_name=end_date_name,
        save=save,
        verbose=verbose
    )

    # Retreive raw data and perform inference.
    raw_path = _generate_raw_proc_path(
        config_dict=DATA_PREP_CONFIG.processing_info[data_key],
        df_type="raw"
    )
    df_raw = pd.read_parquet(raw_path)
    df_preds = pd.DataFrame({
        "preds": inference_pipeline.predict(df_raw)
    })
    if save:
        save_path = _get_scores_save_path(
            data_key=data_key,
            end_date_name=end_date_name
        )
        _logger.info(
            f"Saving scores for data_key={data_key} and date="
            f"{end_date_name} to {save_path}."
        )
        df_preds.to_parquet(save_path)
    return df_preds


import numpy
import os
import cv2
import math

my_img = cv2.imread("Fig02.tif",0)
arr = numpy.shape(my_img)

gamma =[0.6, 0.9, 1.3, 1.8]
length = len(gamma)
print(length)

def g_power(array,a):
    arr1 = numpy.shape(array)
    my_img1 = numpy.ones((arr1[0],arr1[1]), dtype = numpy.float64)
    for i in range (length):
        for x in range(arr[0]):
            for y in range(arr[1]):
                temp = my_img[x][y]**a
                my_img1[x][y]=temp
    return my_img1

def log_n(array):
    arr1 = numpy.shape(array)
    my_img2 = numpy.ones((arr1[0],arr1[1]), dtype = numpy.float64)
    for i in range(arr[0]):
        for j in range(arr[1]):
            temp = numpy.log10(array[i][j]+1)
            my_img2[i][j] = temp
    return my_img2

def normalize(array):
    arr1 = numpy.shape(array)
    my_img3 = numpy.ones((arr1[0],arr1[1]), dtype = numpy.float64)
    minimum = numpy.min(array)
    maximum = numpy.max(array)
    for i in range(arr[0]):
        for j in range(arr[1]):
            temp = ((array[i][j]-minimum)/(maximum-minimum))*255
            my_img3[i][j] = temp
    my_img3 = numpy.uint8(my_img3)
    return my_img3

for i in range (length):

    if i==3:
        a=g_power(my_img,gamma[i])
        my_img1 = numpy.uint8(a)
        cv2.imshow("1",my_img1)
        c = normalize(a)
        cv2.imshow("4",c)
        b=log_n(c)
        my_img2 = numpy.uint8(b)
        cv2.imshow("2",my_img2)


"""
This examples runs the OpEn Agent, a classical, MPC based agent using [OpEn](https://alphaville.github.io/optimization-engine/).

NOTE: You will need to install Rust to run this example. See https://rustup.rs/ for instructions.
"""

import importlib
import logging
import math

import gym

from smarts.core.utils.episodes import episodes

try:
    open_agent = importlib.import_module("open_agent")
except ModuleNotFoundError as e:
    raise ModuleNotFoundError(
        f"Ensure that the open-agent has been installed with `pip install open-agent"
    )

from examples import default_argument_parser

logging.basicConfig(level=logging.INFO)

AGENT_ID = "Agent-007"

def main(scenarios, sim_name, headless, num_episodes, seed):
    open_agent_spec = open_agent.entrypoint(debug=False, aggressiveness=3)
    env = gym.make(
        "smarts.env:hiway-v0",
        scenarios=scenarios,
        agent_specs={AGENT_ID: open_agent_spec},
        sim_name=sim_name,
        headless=headless,
        visdom=False,
        timestep_sec=0.1,
        sumo_headless=True,
        seed=seed,
        # envision_record_data_replay_path="./data_replay",
    )

    for episode in episodes(n=num_episodes):
        agent = open_agent_spec.build_agent()

        observations = env.reset()
        episode.record_scenario(env.scenario_log)

        dones = {"__all__": False}
        while not dones["__all__"]:
            agent_obs = observations[AGENT_ID]
            agent_action = agent.act(agent_obs)
            observations, rewards, dones, infos = env.step({AGENT_ID: agent_action})
            episode.record_step(observations, rewards, dones, infos)

        del agent

    env.close()

if __name__ == "__main__":
    parser = default_argument_parser("OpEn-trajectory-optimizer-example")
    args = parser.parse_args()

    main(
        scenarios=args.scenarios,
        sim_name=args.sim_name,
        headless=args.headless,
        num_episodes=args.episodes,
        seed=args.seed,
    )


from fpdf import FPDF

class Bill:
    """
    Object that contains data abut a bill such as total amount
    and period of the bill.
    """

    def __init__(self, amount, period):
        self.amount = amount
        self.period = period

class Flatmate:
    """
    Object that contains data about the flatmates such as name,
    days in house and how much they each pay.
    """

    def __init__(self, name, days_in_house):
        self.name = name
        self.days_in_house = days_in_house

    def pays(self, bill, flatmate2):
        weight = self.days_in_house / (self.days_in_house + flatmate2.days_in_house)
        to_pay = weight * bill.amount
        return to_pay

class PdfReport:
    """
    Creates a PDF report that contains data about the flatmate
    such as their name, their due amount and the period of the
    bill.
    """

    def __init__(self, filename):
        self.filename = filename

    def generate(self, flatmate1, flatmate2, bill):
        pdf = FPDF(orientation='P', unit='pt', format='A4')
        pdf.add_page()
        # insert title
        pdf.set_font(family='Times', size=24, style='B')
        pdf.cell(w=0, h=80, txt='Flatmates Bill', border=1, align='C', ln=1)
        # insert period label and value
        pdf.cell(w=100, h=40, txt='Period:', border=1)
        pdf.cell(w=150, h=40, txt=bill.period, border=1, ln=1)

        pdf.output(self.filename)

the_bill = Bill(120, "April 2021")
John = Flatmate("John", 20)
Matt = Flatmate("Matt", 25)

print("John pays", John.pays(the_bill, Matt))
print("Matt pays", Matt.pays(the_bill, John))

pdf_report = PdfReport(filename='Report1.pdf')
pdf_report.generate(flatmate1=John, flatmate2=Matt, bill=the_bill)


import scrapy

class SRealitySpider(scrapy.Spider):
    name = 'sreality_spider'
    # comprehension list of 25 (500 items) URLs  that the spider will start crawling from.    ( crawler sign)
    start_urls = ['https://www.sreality.cz/en/search/for-sale/apartments?page=' + str(i) + '&_escaped_fragment_='
                  for i in range(25)]

    def parse(self, response):
        # gets titles
        titles = response.xpath('.//*[@class="title"]/span/text()').getall()
        # gets url parts
        url_parts = response.xpath('.//*[@class="title"]/@href')
        for title, url_part in zip(titles, url_parts):
            # gets desired images
            img = response.xpath('.//a[@href="' + url_part.root + '"]/img/@src').getall()
            yield {
                    'url_part': url_part.root, "title": title, "img": img
                }


from django.contrib.auth.decorators import login_required
from django.core import serializers
from django.shortcuts import render
from django.http import JsonResponse

# Create your views here.
from chat.models import Thread, ChatMessage

@login_required
def messages_page(request):
    threads = Thread.objects.by_user(user=request.user).prefetch_related('chatmessage_thread').order_by('timestamp')
    context = {
        'Threads': threads
    }
    return render(request, 'messages.html', context)

def get_picture_url(request, thread_id):
    #thread_id = request.GET.get(thread_id)  # Assuming you pass the thread ID as a query parameter
    thread = Thread.objects.get(id=thread_id)
    first_person_picture_url = thread.first_person_profile().picture.url
    second_person_picture_url = thread.second_person_profile().picture.url
    first_person_id = thread.first_person.id
    second_person_id = thread.second_person.id

    messages = ChatMessage.objects.filter(thread_id=thread_id)

        # Serialize the messages into JSON format
    serialized_messages = serializers.serialize('json', messages)

    data = {
        'first_person_picture_url': first_person_picture_url,
        'second_person_picture_url': second_person_picture_url,
        'first_person_id': first_person_id,
        'second_person_id': second_person_id,
        'messages': serialized_messages
    }
    return JsonResponse(data)


# Import pacchetti
import streamlit as st
import streamlit_authenticator as stauth #pip install streamlit_authenticator==0.1.5
from streamlit_lottie import st_lottie

import os
import json
import pickle
from pathlib import Path
import csv
import datetime
import time

import pandas as pd
import pygwalker as pyg

import cedolib as clib

# import PyPDF2
import PyPDF2

# import pdfminer.six
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainer

versione = "0.0.7"

# Configurazione PAGINA
st.set_page_config(
  page_title="AFEPEC",
  page_icon="ðŸ“Š",
  layout="wide",
  initial_sidebar_state="expanded",
  menu_items={
    'Get Help': None,
    'Report a bug': 'mailto:r.moscato@ilivetech.it',
    'About': f'AFEPEC v{versione} - ROSARIOSoft - Un software MIRACOLOSO!'
      }
  )

# Nascondo Hamburger Menu e Footer
hide_st_style = """

              """

st.markdown(hide_st_style, unsafe_allow_html=True)

# Load Data
def load_data(url):
  df = pd.read_csv(url)
  return df

# Display PyGWalker
def load_config(file_path):
  with open(file_path, 'r') as config_file:
    config_str = config_file.read()
  return config_str

# Loading Lottie Files
def load_lottiefile(filepath: str):
  with open(filepath, "r") as f:
    return json.load(f)

def get_files(path):
  files = []
  for file in os.listdir(path):
      if os.path.isfile(os.path.join(path, file)):
          files.append(file)
  return files

def remove_files(path, files):
  for file in files:
      os.remove(os.path.join(path, file))

def download_file(file_path="check_cedolini.csv"):
	with open(file_path, 'rb') as file:
		file_data = file.read()
	st.sidebar.download_button('Download Risultati', file_data, file_name="check_cedolini.csv", mime="text/csv")

# Funzione che cancella tutti i file di una directory
def delete_files_in_directory(directory):
    for filename in os.listdir(directory):
        file_path = os.path.join(directory, filename)
        if os.path.isfile(file_path):
            os.remove(file_path)

# INIZIO MAIN (Parte di FRONT-END)
def main():
  st.sidebar.image('./immagini/logo')
  authenticator.logout("Logout", "sidebar")
  st.sidebar.subheader(f"Buongiorno {name}")

  st.title("AFEPEC")
  st.header("Analizzatore FErie e PErmessi dai Cedolini")

  folder_path = "./Cedolini"

  if not os.path.exists("Cedolini"):
    os.makedirs("Cedolini")

  menu_principale = st.sidebar.selectbox("Selezionare una voce", ["Home", "Gestione File", "Elaborazione", "Download", "Analisi"])

  if menu_principale == "Home":
    # Menu Gestione File
    lottie_hr = load_lottiefile("./immagini/human-resources-approval-animation.json")
    st_lottie(
      lottie_hr,
      reverse=False,
      loop=True,
      quality="medium",
      height=640,
      width=800,
      key="hello")

  elif menu_principale == "Gestione File":
    # Menu Gestione File
    st.subheader("Gestione File")
    spiega_file = """
    In questa sezione Ã¨ possibile:

Importare i file da elaborare successivamente

Cancellare uno alla volta i file che non interessano piÃ¹

Cancellare in una volta tutti i file importati precedentemente

    """
    st.markdown(spiega_file, unsafe_allow_html=True)
    selected_option = st.sidebar.selectbox("", ("Upload", "Eliminazione"))

    if selected_option == "Upload":

      files = st.sidebar.file_uploader("Selezionare i files da importare", type = ['pdf'], accept_multiple_files=True)
      if files is not None:
        for file in files:
          with open(os.path.join(folder_path, file.name), "wb") as f:
            f.write(file.getbuffer())

    elif selected_option == "Eliminazione":

      files = get_files(folder_path)
      if files is not None:
        tipo_canc = st.sidebar.radio("Selezionare il tipo di eliminazione:", ("Tutti i File", "File Singoli"))
        if tipo_canc=="Tutti i File":
          if st.sidebar.button("Cancella TUTTO"):
            delete_files_in_directory('./Cedolini')
            st.sidebar.success("Eliminazione completata")
        else:
          selected_files = st.sidebar.multiselect("Selezionare i files da eliminare singolarmente:", files)
          if st.sidebar.button("Eliminazione file selezionato/i"):
            remove_files(folder_path, selected_files)
            st.sidebar.success("Eliminazione completata")
            files = get_files(folder_path)

  elif menu_principale == "Elaborazione":
    presenza_file = get_files(folder_path)
    st.subheader("Elaborazione Cedolini")
    spiega_elaborazione = """
    In questa sezione Ã¨ possibile esegure la ricerca di **ferie** e **permessi** all'interno dei **cedolini** importati in precedenza.
    Il risultato di tale operazione sarÃ  un file *csv* (da aprire con excel) scaricabile selezionando la voce **Download** del menu.
    Per una sua rapida analisi selezionare, invece, la voce **Analisi**.
    """
    st.markdown(spiega_elaborazione, unsafe_allow_html=True)

    if len(presenza_file)==0:
      st.sidebar.warning("Importare dei file da gestire")
    else:
      start_time = time.time()
      # Specifico la directory dei Cedolini
      directory = "Cedolini"
      # Get all file names in the directory
      files = os.listdir(directory)

      # Inizializzazioni
      final_result = [['Codice F.', 'Mese', 'Ferie AP', 'Ferie Maturate', 'Ferie Godute', 'Ferie Saldo', 'Permessi AP', 'Permessi Maturati', 'Permessi Goduti', 'Permessi Saldo']]

      scartati = []

      count_pages = 0
      numero_processamenti = 0
      numero_scartati = 0
      numero_altro_pdf = 0 #In realtÃ  Ã¨ il numero di pdf che sono cedolini

      print("Elaborazione in corso...")
      # Cerco in tutti i files della directory
      for file in files:
        if file.endswith(".pdf"):

          input_file = os.path.join(directory, file)
          #st.write(input_file)
          pdf_document = input_file

          # Conto il numero di  pagine
          with open(pdf_document, "rb") as file:
            pdf_file = PyPDF2.PdfReader(file)
            count_pages = len(pdf_file.pages)
            #st.write(count_pages)
            #print(f"Documento: {input_file} - Numero di pagine:{count_pages}")

          #Estraggo gli elementi da ogni documento incluso nella directory
          text = ""
          for page_layout in extract_pages(input_file):
            for element in page_layout:
              if isinstance(element, LTTextContainer):
                text += element.get_text()
                #Stampa tutto il contenuto
                #print(text)
                #Stampa tutti gli elementi individuati
                #print(element)

          outcome = []

          try:
            numero_processamenti += 1

            ### ZUCCHETTI ###
            if "Zucchetti" in text:
              #st.write(count_pages, numero_altro_pdf, input_file, pdf_document, outcome, final_result)
              numero_altro_pdf, final_result =  clib.zucchetti(count_pages, numero_altro_pdf, input_file, pdf_document, outcome, final_result)
              #st.write(numero_altro_pdf)

            ### SALCIARINI
            elif "SALCIARINI" in text:
              numero_altro_pdf, final_result = clib.salciarini(text, numero_altro_pdf, input_file, outcome, final_result)

            ### SEAC
            else:
              numero_altro_pdf, final_result = clib.seac(text, numero_altro_pdf, input_file, outcome, final_result)

          # Gestione File PDF NON Riconosciuti
          except:
            #print(f"\n!Errore: documento: {file} NON RICONOSCIUTO\n")
            scartati.append(input_file)
            numero_scartati += 1

      # Creazione REPORT
      current_date = datetime.date.today()
      end_time = time.time()
      execution_time = end_time - start_time
      report =f"""
      ==REPORT ESECUZIONE PROCESSO ===================================
      - Data Esecuzione: {current_date}
      - Tempo Esecuzione: circa {round(execution_time)} secondi
      - Numero totale pdf processati:{numero_processamenti}
      - Numero cedolini gestiti: {numero_altro_pdf}
      - Numero pdf altro tipo: {numero_processamenti-numero_altro_pdf}
      - Numero documenti non letti: {numero_scartati}
      """
      st.markdown(report, unsafe_allow_html=True)

      if numero_scartati > 0:
        st.write("- Documenti scartati:", str(len(scartati)))
        st.write("\t",scartati)

      # Scrittura file di OUTPUT
      with open('check_cedolini.csv', 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerows(final_result)

      # Gestione DATE e formattazione file OUTPUT
      esito_conversione = ""
      try:
      	esito_conversione = clib.converti_date("check_cedolini.csv")
      except:
        esito_conversione = "Negativo"

      st.write(f"- Conversione Formato: {esito_conversione}")
      st.write("- Output file: 'check_cedolini.csv'")
      st.write("== FINE REPORT ===================================================")

      print("\nEsecuzione terminata:")
      print(f"- Esito Conversione: {esito_conversione}")
      print("- Output file: check_cedolini.csv")
      print("- Report file: report_cedolini.txt\n")

  elif menu_principale == "Download":
    st.subheader("Download File")
    st.markdown("Cliccare sul bottone laterale per scaricare il file **csv** con i risultati dell'elaborazione.")
    if os.path.isfile("check_cedolini.csv"):
      download_file()
      #data = pd.read_csv("./check_cedolini.csv", encoding='utf-8', sep=";")
      #st.title("Data Editor")
      #edited_data = st.data_editor(data)
      #st.table(data)
    else:
      st.sidebar.warning("Non risultano presenti file da scaricare.")

  #elif menu_principale == "Analisi":
  else:
    st.subheader("Analisi dei Risultati")
    if os.path.isfile("check_cedolini.csv"):
      try:
        df = load_data("check_cedolini.csv")
        #config = load_config('config.json')
        pyg.walk(df, env='Streamlit', dark='light')#, spec=config)
      except:
        st.warning("Problemi con la visualizzazione del file di output. Si consiglia di scaricarlo e analizzarlo con un foglio di calcolo.")
    else:
      st.sidebar.warning("Non risultano presenti file da analizzare.")

# --- USER AUTHENTICATION ---
names = ["Rosario Moscato", "Mario Mastroianni", "Elvira Floridi", "Arianna D'Urbano", "Giorgio Grappelli"]
usernames = ["rmos", "mmas", "eflo", "adur", "ggra"]

# Load hashed passwords
file_path = Path(__file__).parent / "hashed_pw.pkl"
with file_path.open("rb") as file:
  hashed_passwords = pickle.load(file)

authenticator = stauth.Authenticate(names, usernames, hashed_passwords, "afepec_webapp", "abcdef", cookie_expiry_days = 30)

name, authentication_status, username = authenticator.login("AFEPEC - Login", "main")

if authentication_status == False:
  st.error("Username e/o Password non corretti")

if authentication_status == None:
  st.warning("Inserire Username e Password")

if authentication_status:

  if __name__ == "__main__":
      main()


import os
import math
from pathlib import Path
import pymoto as pym
import matplotlib.pyplot as plt
import matplotlib.patches as plt_patch
import numpy as np

# UTILITIES
def unwrap(angle, period=2*math.pi):
    """Unwrap a phase angle to give a continuous curve

    Parameters
    ----------
    angle : array_like
        Array of angles to be unwrapped
    period : float, optional
        Period (defaults to `2*pi`)

    Returns
    -------
    angle_out : array_like
        Output array, with jumps of period/2 eliminated

    Examples
    --------
    import numpy as np
    theta = [5.74, 5.97, 6.19, 0.13, 0.35, 0.57]
    unwrap(theta, period=2 * np.pi)
    [5.74, 5.97, 6.19, 6.413185307179586, 6.633185307179586, 6.8531853071795865]

    """
    dangle = np.diff(angle)
    dangle_desired = (dangle + period/2.) % period - period/2.
    correction = np.cumsum(dangle_desired - dangle, axis=-1)
    angle[..., 1:] += correction
    return angle

class GeneralPlot:
    """ Plot helper for plots that are interactively updated """
    def __init__(self, ax, *args, **kwargs):
        self.ax = ax
        self.args = args
        self.kwargs = kwargs
        self.lines = None
        self.shape = None

    def update(self):
        raise NotImplementedError("Update function should be implemented")

    @staticmethod
    def get_data(obj):
        return np.asarray(obj.state if hasattr(obj, 'state') else obj)

    def get_args(self, idx):
        # Parse arguments
        arguments = []
        for a in self.args:
            try:
                aa = np.asarray(a)
                assert aa.shape[len(self.shape)] == self.shape
                arguments.append(aa[idx[:len(self.shape)]])
            except:
                arguments.append(a)
        return arguments

    def get_kwargs(self, idx):
        # Parse keyword arguments
        kwarguments = {}
        for k, a in self.kwargs.items():
            try:
                aa = np.asarray(a)
                assert aa.shape[0:len(self.shape)] == self.shape
                kwarguments[k] = aa[idx[:len(self.shape)]]
            except:
                if k == "label":
                    a = a + " " + str(idx)
                kwarguments[k] = a
        return kwarguments

    def init_lines(self, shape):
        if self.lines is not None:
            return
        self.shape = shape
        self.lines = np.ndarray(shape=self.shape, dtype=np.object)
        for (idx, line) in np.ndenumerate(self.lines):
            arguments = self.get_args(idx)
            kwarguments = self.get_kwargs(idx)
            self.lines[idx] = self.ax.plot([], [], *arguments, **kwarguments)[0]

class UpdatePlotXY(GeneralPlot):
    """ Helper to update XY-data plots """
    def __init__(self, ax, *args, complex_data=None, x_data=None, y_data=None, which_x=Ellipsis, which_y=Ellipsis, **kwargs):
        super().__init__(ax, *args, **kwargs)
        self.which_x = which_x
        self.which_y = which_y
        if complex_data is None and x_data is None and y_data is None:
            return
        assert (complex_data is None) != (x_data is None), "Either (xdata, ydata) or complexdata must be set"
        assert (complex_data is None) != (y_data is None), "Either (xdata, ydata) or complexdata must be set"
        self.complexmode = complex_data is not None
        self.complex_data = complex_data
        self.x_data, self.y_data = x_data, y_data

    def update(self, xdata=None, ydata=None):
        if xdata is None or ydata is None:
            if self.complexmode:
                compldat = self.get_data(self.complex_data)
                xdata, ydata = np.real(compldat), np.imag(compldat)
            else:
                xdata, ydata = self.get_data(self.x_data), self.get_data(self.y_data)
        self.set_xydata(xdata[self.which_x], ydata[self.which_y])

    def set_xydata(self, xdata, ydata):
        # Parse into numpy array
        xdata, ydata = np.asarray(xdata), np.asarray(ydata)

        # Check if x and y sizes are conforming
        if xdata.shape != ydata.shape:
            if xdata.size == 1:
                xdata = np.ones_like(ydata)*xdata.flatten()
            elif ydata.size == 1:
                ydata = np.ones_like(xdata)*ydata.flatten()
            elif xdata.shape[-1] == ydata.shape[-1]:
                if np.prod(xdata.shape[:-1], dtype=int) == 1:
                    xdata = np.ones_like(ydata) * xdata.flatten()
                elif np.prod(ydata.shape[:-1], dtype=int) == 1:
                    ydata = np.ones_like(xdata) * ydata.flatten()
            elif xdata.shape[0] == ydata.shape[0]:
                if np.prod(xdata.shape[1:], dtype=int) == 1:
                    xdata = np.broadcast_to(xdata, reversed(ydata.shape))
                    ydata = ydata.T
                elif np.prod(ydata.shape[1:], dtype=int) == 1:
                    ydata = np.broadcast_to(ydata, reversed(xdata.shape))
                    xdata = xdata.T

        self.init_lines(xdata.shape[:-1])
        for (idx, line) in np.ndenumerate(self.lines):
            line.set_xdata(xdata[idx])
            line.set_ydata(ydata[idx])

    def set_xdata(self, xdata):
        xdata = np.asarray(xdata)
        self.init_lines(xdata.shape[:-1])
        for (idx, line) in np.ndenumerate(self.lines):
            line.set_xdata(xdata[idx])

    def set_ydata(self, ydata):
        ydata = np.asarray(ydata)
        self.init_lines(ydata.shape[:-1])
        for (idx, line) in np.ndenumerate(self.lines):
            line.set_xdata(ydata[idx])

class UpdatePlotCircle(GeneralPlot):
    """ Helper for plots with circles """
    def __init__(self, ax, midp, radius, *args, which=Ellipsis, **kwargs):
        super().__init__(ax, *args, **kwargs)
        self.midp = midp
        self.radius = radius
        self.circles = None
        self.which = which

    def init_circles(self, shape):
        if self.circles is not None:
            return
        if len(shape)>1:
            self.shape = shape[:-1]
        else:
            self.shape = shape
        self.circles = np.ndarray(shape=shape, dtype=np.object)
        for (idx, circ) in np.ndenumerate(self.circles):
            # Parse arguments
            arguments = self.get_args(idx)
            kwarguments = self.get_kwargs(idx)
            self.circles[idx] = plt_patch.Circle((0, 0), 0, *arguments, **kwarguments)
            self.ax.add_artist(self.circles[idx])

    def update(self):
        midpt = self.get_data(self.midp)
        x, y = np.real(midpt), np.imag(midpt)
        r = self.get_data(self.radius)
        x = np.asarray(x)[self.which]
        y = np.asarray(y)[self.which]
        r = np.asarray(r)[self.which]
        assert x.shape == y.shape == r.shape, "Midpoints and radius must be of equal size"
        self.init_circles(x.shape)

        for (idx, circ) in np.ndenumerate(self.circles):
            circ.center = x[idx], y[idx]
            circ.set_radius(r[idx])

class UpdatePlotInterpXY(UpdatePlotXY):
    """ Interpolates parametric xy-data (x(w), y(w)) at points w_i """
    def __init__(self, ax, w_inter_pts,  *args, complex_data=None, x_data=None, y_data=None, w_data=None, which_x=Ellipsis, which_y=Ellipsis, **kwargs):
        super().__init__(ax, *args, complex_data=complex_data, x_data=x_data, y_data=y_data, which_x=which_x, which_y=which_y, **kwargs)
        self.interpolation_points = w_inter_pts
        self.w_data = w_data

    def update(self):
        if self.complexmode:
            compldat = self.get_data(self.complex_data)
            xdata, ydata = np.real(compldat), np.imag(compldat)
        else:
            xdata, ydata = self.get_data(self.x_data), self.get_data(self.y_data)
        wdata = self.get_data(self.w_data)
        wdata = np.imag(wdata) if np.any(np.iscomplex(wdata)) else wdata
        w_inter = self.get_data(self.interpolation_points)
        w_inter = np.imag(w_inter) if np.any(np.iscomplex(w_inter)) else w_inter
        # Remove frequencies out of bound
        w_inter = w_inter[w_inter >= np.min(wdata)]
        w_inter = w_inter[w_inter <= np.max(wdata)]

        x_inter = np.interp(w_inter, wdata, xdata[self.which_x])
        y_inter = np.interp(w_inter, wdata, ydata[self.which_y])
        self.set_xydata(x_inter, y_inter)

class UpdatePlotInterpY(UpdatePlotXY):
    """ Interpolates ydata for y(x) at x, with given y(x_i)"""
    def __init__(self, ax, x_inter_pts,  *args, complex_data=None, x_data=None, y_data=None, which_x=Ellipsis, which_y=Ellipsis, **kwargs):
        super().__init__(ax, *args, complex_data=complex_data, x_data=x_data, y_data=y_data, which_x=which_x, which_y=which_y, **kwargs)
        self.interpolation_points = x_inter_pts

    def update(self, xdata=None, ydata=None):
        if xdata is None or ydata is None:
            if self.complexmode:
                compldat = self.get_data(self.complex_data)
                xdata, ydata = np.real(compldat), np.imag(compldat)
            else:
                xdata, ydata = self.get_data(self.x_data), self.get_data(self.y_data)

        x_inter = self.get_data(self.interpolation_points)
        x_inter = np.imag(x_inter) if np.any(np.iscomplex(x_inter)) else x_inter
        # Remove frequencies out of bound
        x_inter = x_inter[x_inter >= np.min(xdata)]
        x_inter = x_inter[x_inter <= np.max(xdata)]

        xdata, ydata, x_inter = xdata[self.which_x], ydata[self.which_y], x_inter[self.which_x]
        y_inter = np.zeros((*ydata.shape[:-1], x_inter.shape[0]))
        for idx in np.ndindex(ydata.shape[:-1]):
            y_inter[idx] = np.interp(x_inter, xdata, ydata[idx])
        self.set_xydata(x_inter, y_inter)

# PLOTTING MODULES
class PlotNyquist(pym.Module):
    def _prepare(self, stab_lim=6.0, saveto="out/nyquist.png", iter=0, xlim=[-3, 1], ylim=[-2, 2], title="Nyquist"):
        self.stab_lim = stab_lim
        if saveto is not None:
            Path(saveto).parent.mkdir(parents=True, exist_ok=True)
            self.saveloc, self.saveext = os.path.splitext(saveto)
        else:
            self.saveloc, self.saveext = None, None
        self.iter = iter
        self.plotlist = []
        self.title = title

        plt.ion()
        self.fig, _ = plt.subplots(1, 1)
        self.ax = self.fig.axes[0]
        self.ax.set_xlim(xlim)
        self.ax.set_ylim(ylim)
        self.ax.set_aspect('equal')

        self.ax.set_xlabel('Real')
        self.ax.set_ylabel('Imag')
        if stab_lim is not None:
            UpdatePlotCircle(self.ax, -1.0, 1/(10**(stab_lim/20)), label=f"{stab_lim} dB", fill=False, linestyle='--', color='k').update()
            UpdatePlotXY(self.ax, 'k+', complex_data=-1).update()
        self.draw()
        self.leg = False

        plt.show()

    def __del__(self):
        if hasattr(self, 'fig'):
            plt.close(self.fig)

    def add_xyplot(self, *args, complex_data=None, x_data=None, y_data=None, **kwargs):
        self.plotlist.append(UpdatePlotXY(self.ax, *args, complex_data=complex_data, x_data=x_data, y_data=y_data, **kwargs))

    def add_circles(self, midp, radius, *args, **kwargs):
        self.plotlist.append(UpdatePlotCircle(self.ax, midp, radius, *args, **kwargs))

    def add_interp(self, complex_data, freq_data, freq_points, *args, **kwargs):
        self.plotlist.append(UpdatePlotInterpXY(self.ax, complex_data, freq_data, freq_points, *args, **kwargs))

    def draw(self):
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()

    def _response(self, *g):
        self.ax.set_title(f"{self.title}, It. {self.iter}")
        [p.update() for p in self.plotlist]
        if not self.leg:
            self.ax.legend()
            self.leg = True
        self.draw()

        if self.saveloc is not None:
            self.fig.savefig("{0:s}.{1:04d}{2:s}".format(self.saveloc, self.iter, self.saveext))

class PlotSensitivity(pym.Module):
    def _prepare(self, stab_lim=6.0, saveto="out/sensitivity.png", iter=0, ylim=None):
        self.stab_lim = stab_lim
        if saveto is not None:
            Path(saveto).parent.mkdir(parents=True, exist_ok=True)
            self.saveloc, self.saveext = os.path.splitext(saveto)
        else:
            self.saveloc, self.saveext = None, None
        self.iter = iter
        self.plotlist = []
        self.ylist = []
        self.rangelist = []
        self.leg = False
        self.ylim = ylim

        # Signal for the limits
        self.s_ylimits = pym.Signal('vertical limits')
        self.s_xlimits = pym.Signal('horizontal limits')

        # Create figure
        plt.ion()
        self.fig, _ = plt.subplots(1, 1)
        self.ax = self.fig.axes[0]

        self.ax.set_xscale('log')
        self.ax.grid(True, which='both')
        self.ax.set_xlabel("Frequency (rad/s)")
        self.ax.set_ylabel("Amplitude (dB)")
        plt.show()

        # Add limit line
        self.add_horizontal_line(self.stab_lim, 'k--', label="Stability limit")

    def __del__(self):
        if hasattr(self, 'fig'):
            plt.close(self.fig)

    def add_vertical_line(self, s_x, *args, **kwargs):
        self.plotlist.append(UpdatePlotXY(self.ax, *args, x_data=s_x, y_data=self.s_ylimits, **kwargs))

    def add_horizontal_line(self, s_y, *args, **kwargs):
        self.plotlist.append(UpdatePlotXY(self.ax, *args, x_data=self.s_xlimits, y_data=s_y, **kwargs))

    def add_limit_line(self, s_y, range_inds, *args, s_x=None, **kwargs):
        n = len(self.rangelist)
        s_xrange = pym.Signal(f"range{n}")
        self.rangelist.append((range_inds, s_xrange, s_x))
        self.add_xyplot(s_xrange, s_y, *args, **kwargs)

    def add_xyplot(self, y_data, *args, s_x=None, **kwargs):
        self.ylist.append(y_data)
        self.plotlist.append(UpdatePlotXY(self.ax, *args, x_data=s_x if s_x is not None else self.sig_in[0], y_data=y_data, **kwargs))

    def add_interp(self, y_data, x_points, *args, s_x=None, **kwargs):
        self.plotlist.append(UpdatePlotInterpY(self.ax, s_x if s_x is not None else self.sig_in[0], y_data, x_points, *args, **kwargs))

    def draw(self):
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()

    def _response(self, w_range):
        self.ax.set_title('Sensitivity, It. {}'.format(self.iter))

        # Update ranges
        for (r, s, sx) in self.rangelist:
            if sx is None:
                s.state = np.array([w_range[r[0]], w_range[r[1]]])
            else:
                xdata = sx.state if hasattr(sx, 'state') else sx
                s.state = np.array([xdata[r[0]], xdata[r[1]]])

        # Find upper and lower limits
        wmin = np.min(w_range)
        wmax = np.max(w_range)
        self.s_xlimits.state = np.array([wmin, wmax])
        ymin = 0.0
        ymax = 10.0
        for s_y in self.ylist:
            ydata = s_y.state if hasattr(s_y, 'state') else s_y
            ymin = np.floor((min(np.min(ydata), ymin) - 5.0) / 10.0) * 10.0
            ymax = np.ceil((max(np.max(ydata), ymax) + 5.0) / 10.0) * 10.0
        if self.ylim:
            ymin, ymax = self.ylim
        self.s_ylimits.state = np.array([ymin, ymax])

        [p.update() for p in self.plotlist]
        self.ax.set_xlim([wmin, wmax])
        self.ax.set_ylim([ymin, ymax])
        if not self.leg:
            self.ax.legend()
            self.leg = True
        self.draw()

        if self.saveloc is not None:
            self.fig.savefig("{0:s}.{1:04d}{2:s}".format(self.saveloc, self.iter, self.saveext))

class PlotBode(pym.Module):
    def _prepare(self, saveto="out/bode.png", iter=0, norm_scale='dB', phase_wrap=False):
        if saveto is not None:
            Path(saveto).parent.mkdir(parents=True, exist_ok=True)
            self.saveloc, self.saveext = os.path.splitext(saveto)
        else:
            self.saveloc, self.saveext = None, None
        self.iter = iter
        self.plotlist = []
        self.transferlist = []
        self.interplist = []
        self.leg = False
        self.norm_scale = norm_scale
        self.phase_wrap = phase_wrap

        # Signal for the limits
        self.s_ylimits_nrm = pym.Signal('vertical limits norm')
        self.s_ylimits_phs = pym.Signal('vertical limits phase')
        self.s_xlimits = pym.Signal('horizontal limits')

        # Create figure
        plt.ion()
        self.fig, _ = plt.subplots(2, 1, sharex=True)
        self.ax_nrm, self.ax_phs = self.fig.axes

        if self.norm_scale == 'log':
            self.ax_nrm.set_yscale('log')
        self.ax_nrm.set_xscale('log')
        self.ax_nrm.grid(True, which='both')
        self.ax_nrm.set_ylabel('Amplitude')

        self.ax_phs.set_xscale('log')
        self.ax_phs.grid(True, which='both')
        self.ax_phs.set_ylabel('Phase')
        self.ax_phs.set_xlabel('Frequency')
        plt.show()

    def __del__(self):
        if hasattr(self, 'fig'):
            plt.close(self.fig)

    def draw(self):
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()

    def is_tagged(self, tag):
        both_tags = ['both', 'all']
        norm_tags = ['norm', 'nrm', 'amplitude', 'a']
        phase_tags = ['phase', 'phs', 'pha', 'p', 'angle']
        is_both = tag.lower() in both_tags
        is_norm = is_both or tag.lower() in norm_tags
        is_phase = is_both or tag.lower() in phase_tags

        if not (is_phase or is_norm):
            raise ValueError(f"Invalid argument 'which' = {tag}")
        return is_norm, is_phase

    def add_vertical_line(self, s_x, *args, which='norm', **kwargs):
        is_norm, is_phase = self.is_tagged(which)
        if is_norm:
            self.plotlist.append(UpdatePlotXY(self.ax_nrm, *args, x_data=s_x, y_data=self.s_ylimits_nrm, **kwargs))
        if is_phase:
            self.plotlist.append(UpdatePlotXY(self.ax_phs, *args, x_data=s_x, y_data=self.s_ylimits_phs, **kwargs))

    def add_horizontal_line(self, s_y, *args, which='norm', **kwargs):
        is_norm, is_phase = self.is_tagged(which)
        if is_norm:
            self.plotlist.append(UpdatePlotXY(self.ax_nrm, *args, x_data=self.s_xlimits, y_data=s_y, **kwargs))
        if is_phase:
            self.plotlist.append(UpdatePlotXY(self.ax_phs, *args, x_data=self.s_xlimits, y_data=s_y, **kwargs))

    def add_transfer(self, s_G, *args, **kwargs):
        self.transferlist.append((s_G, UpdatePlotXY(self.ax_nrm, *args, **kwargs), UpdatePlotXY(self.ax_phs, *args, **kwargs)))

    def add_interp(self, s_G, x_points, *args, s_x=None, **kwargs):
        if s_x is None:
            s_x = self.sig_in[0]
        self.interplist.append((s_G, UpdatePlotInterpY(self.ax_nrm, x_points, *args, x_data=s_x, y_data=0, **kwargs),
                                     UpdatePlotInterpY(self.ax_phs, x_points, *args, x_data=s_x, y_data=0, **kwargs)))

    def _response(self, w_range):
        self.ax_nrm.set_title('Open loop, It. {}'.format(self.iter))

        # Update TFs
        nrm_min = 1e+100
        nrm_max = -1e+100
        pha_min = 1e+100
        pha_max = -1e+100
        w_min = min(w_range)
        w_max = max(w_range)

        for (Gs, line_nrm, line_phs) in self.transferlist:
            G = Gs.state if hasattr(Gs, 'state') else Gs
            G_nrm = np.real(np.sqrt(G*np.conj(G)))
            G_dB = 20*np.log10(G_nrm) if self.norm_scale == 'dB' else G_nrm
            finites = np.isfinite(G_dB)
            G_pha = np.angle(G, deg=True)
            if self.phase_wrap:
                G_pha = unwrap(G_pha, period=360)

            if self.norm_scale == 'dB':
                nrm_min = np.floor((min(np.min(G_dB[finites]), nrm_min) - 5.0) / 10.0) * 10.0
                nrm_max = np.ceil((max(np.max(G_dB[finites]), nrm_max) + 5.0) / 10.0) * 10.0
            else:
                nrm_min = min(np.min(G_dB[finites])*0.9, nrm_min)
                nrm_max = max(np.max(G_dB[finites])*1.1, nrm_max)

            line_nrm.update(w_range, G_dB)
            line_phs.update(w_range, G_pha)
            pha_min = np.floor((min(np.min(G_pha), pha_min) - 5.0) / 45.0) * 45.0
            pha_max = np.ceil((max(np.max(G_pha), pha_max) + 5.0) / 45.0) * 45.0

        for (Gs, line_nrm, line_phs) in self.interplist:
            G = Gs.state if hasattr(Gs, 'state') else Gs
            G_nrm = np.real(np.sqrt(G * np.conj(G)))
            G_dB = 20 * np.log10(G_nrm) if self.norm_scale == 'dB' else G_nrm
            G_pha = np.angle(G, deg=True)
            if self.phase_wrap:
                G_pha = unwrap(G_pha, period=360)
            line_nrm.update(w_range, G_dB)
            line_phs.update(w_range, G_pha)

        self.s_xlimits.state = np.array([w_min, w_max])
        self.s_ylimits_nrm.state = np.array([nrm_min, nrm_max])
        self.s_ylimits_phs.state = np.array([pha_min, pha_max])

        [p.update() for p in self.plotlist]
        self.ax_nrm.set_xlim([w_min, w_max])
        self.ax_nrm.set_ylim([nrm_min, nrm_max])

        self.ax_phs.set_xlim([w_min, w_max])
        self.ax_phs.set_ylim([pha_min, pha_max])
        self.ax_phs.set_yticks(np.arange(np.ceil(pha_min / 90) * 90, np.floor(pha_max / 90) * 90, 90))
        self.ax_phs.set_yticks(np.arange(pha_min, pha_max, 45), minor=True)

        if not self.leg:
            self.ax_phs.legend(loc='right')
            self.leg = True
        self.draw()

        if self.saveloc is not None:
            self.fig.savefig("{0:s}.{1:04d}{2:s}".format(self.saveloc, self.iter, self.saveext))

class Print(pym.Module):
    """ Print signals to console """
    def _prepare(self, showmax=False):
        self.showmax = showmax

    def _response(self, *args):
        for i, xx in enumerate(args):
            if self.showmax and hasattr(xx, 'shape'):
                print(f"Signal \"{self.sig_in[i].tag}\" : [max = {np.max(xx)}] {xx}")
            else:
                print(f"Signal \"{self.sig_in[i].tag}\" : {xx}")

        return []


import sys
import json
from operator import add
from pyspark import SparkContext

review_filepath = sys.argv[1]
output_filepath = sys.argv[2]

ans = {}
sc = SparkContext.getOrCreate()

lines = sc.textFile(review_filepath).map(lambda x: json.loads(x))

# A. The total number of reviews

ans["n_review"] = lines.count()

# B. The number of reviews in 2018

ans["n_review_2018"] = lines.filter(lambda x: x["date"][:4] == "2018").count()

# C. The number of distinct users who wrote reviews

ans["n_user"] = lines.map(lambda x: x["user_id"]).distinct().count()

# D. The top 10 users who wrote the largest numbers of reviews and the number of reviews they wrote

ans["top10_user"] = lines.map(lambda x: (x["user_id"], 1)).reduceByKey(add).sortBy(lambda x: (-x[1], x[0])).take(10)

# E. The number of distinct businesses that have been reviewed

ans["n_business"] = lines.map(lambda x: x["business_id"]).distinct().count()

# F. The top 10 businesses that had the largest numbers of reviews and the number of reviews they had

ans["top10_business"] = lines.map(lambda x: (x["business_id"], 1)).reduceByKey(add).\
    sortBy(lambda x: (-x[1], x[0])).take(10)

with open(output_filepath, 'w+') as output:
    json.dump(ans, output)
output.close()


#!/usr/bin/env python3

import collections
from typing import List, Callable

def find_common(numbers: List[List[int]], comparison: str):
    def inner(numbers, pos) -> List[List[int]]:
        if len(numbers) == 1:
            return numbers[0]

        cmp = collections.defaultdict(int)
        for n in numbers:
            cmp[n[pos]] += 1

        if cmp[0] > cmp[1]:
            want_val = 0
        else:
            want_val = 1

        if comparison == 'min':
            want_val = 1 if want_val == 0 else 0

        if cmp[0] == cmp[1]:
            want_val = 1 if comparison == 'max' else 0

        next_nums = []
        for n in numbers:
            if n[pos] == want_val:
                next_nums.append(n)

        return inner(next_nums, pos+1)
    return inner(numbers, 0)

if __name__ == '__main__':
    numbers = []
    with open('input', 'r') as h:
        for ln in h:
            numbers.append([int(x) for x in ln.strip()])

    bits = len(numbers[0])
    position_stats = [collections.defaultdict(int) for _ in range(bits)]
    for n in numbers:
        for i in range(bits):
            position_stats[i][n[i]] += 1

    mst_cmn = []
    lst_cmn = []
    for pos in position_stats:
        if pos[0] > pos[1]:
            mst_cmn.append(1)
            lst_cmn.append(0)
        else:
            mst_cmn.append(0)
            lst_cmn.append(1)

    mst_num = int(''.join(map(str, mst_cmn)), 2)
    lst_num = int(''.join(map(str, lst_cmn)), 2)

    print(f'Part1: {mst_num * lst_num}')

    oxygen = find_common(numbers[:], 'max')
    co2 = find_common(numbers[:], 'min')

    oxygen = int(''.join(map(str, oxygen)), 2)
    co2 = int(''.join(map(str, co2)), 2)

    print(f'Part2: {oxygen * co2}')


import torch.nn as nn
import torch
import copy
from torch.autograd import Variable
import numpy as np

class Model(nn.Module):
	def __init__(self, topo, lrate, input_size = 1, rnn_net = 'LSTM', optimizer = 'Adam'):
		"""

		"""
		super(Model, self).__init__()
		self.hidden_dim = topo[1]#hidden_dim
		self.input_seq_len = topo[0]
		self.num_outputs = topo[2]
		self.input_size = input_size
		self.n_layers = 1   #len(topo)-2 #n_layers
		# self.batch_size = 1 # no longer required since we are now useing the vectorized implementation
		self.lrate = lrate
		self.optimizer = optimizer
		self.rnn_net = rnn_net
		if rnn_net == 'RNN':
			# self.hidden = torch.ones(self.n_layers, self.batch_size, self.hidden_dim)
			self.rnn = nn.RNN(input_size = self.input_size, hidden_size = topo[1], num_layers = self.n_layers, batch_first= True)
		if rnn_net == 'LSTM':
			# self.hidden = (torch.ones((self.n_layers,self.batch_size,self.hidden_dim)), torch.ones((self.n_layers,self.batch_size,self.hidden_dim)))
			self.rnn = nn.LSTM(input_size = self.input_size, hidden_size = topo[1],num_layers= self.n_layers, batch_first= True)
			# Fully connected layer
		self.fc = nn.Linear(topo[1],topo[2])
		self.topo = topo
		print('Log::Model class init:: ', rnn_net, ' is rnn net')

	def sigmoid(self,z):
		return torch.nn.Sigmoid(z)

	def forward(self, x):
		h_0 = Variable(torch.zeros(self.n_layers, x.shape[0], self.hidden_dim))
		c_0 = Variable(torch.zeros(self.n_layers, x.shape[0], self.hidden_dim))

		batch_size = len(x)
		output_size = self.topo[2]
		# outmain = torch.zeros((batch_size, output_size, 1))

		if(self.rnn_net == 'RNN'):
			ula, h_out = self.rnn(x, h_0)
		elif(self.rnn_net == 'LSTM'):
			# ula, (h_out, _) = self.rnn(x,(h_0, c_0))
			ula, (h_out, _) = self.rnn(x)
			h_out = h_out.view(-1, self.hidden_dim)

		out = self.fc(h_out)
		out = out.view(x.shape[0], self.num_outputs, 1)
		return out #returning the output tensor directly without detaching or deep copying

	def langevin_gradient(self, x, y, w, optimizer):
		self.load_state_dict(w)

		criterion = torch.nn.MSELoss()
		for k in range(1):
			output = self.forward(x)
			optimizer.zero_grad()
			loss = criterion(output, y.view(output.shape))

			loss.backward()
			optimizer.step()

			# print(f'root of MSEloss for k = {k}: {np.sqrt(loss.detach().numpy())}')

		parameters = self.state_dict()
		return parameters

	def dictfromlist(self, param):
		dic = {}
		i = 0
		for name in sorted(self.state_dict().keys()):
			dic[name] = torch.FloatTensor(param[i:i+(self.state_dict()[name]).view(-1).shape[0]]).view(self.state_dict()[name].shape)
			i += (self.state_dict()[name]).view(-1).shape[0]
		#self.loadparameters(dic)
		return dic

	def evaluate_proposal(self, x, w= None):
		backup = copy.deepcopy(self.state_dict())
		self.load_state_dict(w)
		y_pred = self.forward(x)
		self.load_state_dict(backup)
		return copy.deepcopy(y_pred.detach().numpy())

	def addnoiseandcopy(self, w, mean, std_dev):
		d = dict()
		for name in w.keys():
			d[name] = copy.deepcopy(w[name]) + torch.zeros(w[name].size()).normal_(mean = mean, std = std_dev)
		return d

	def getparameters(self, w= None):
		l = np.array([1,2])
		dic = {}
		if w is None:
			dic = self.state_dict()
		else:
			dic = copy.deepcopy(w)
		for name in sorted(dic.keys()):
			l = np.concatenate((l, np.array(copy.deepcopy(dic[name])).reshape(-1)),axis= None)
		l = l[2:]
		return l


import os
import numpy as np
import torch
import argparse
from utils import get_dataset, get_network, get_eval_pool, evaluate_synset, ParamDiffAug, TensorDataset, get_daparam
import copy
import gc

def main():
    parser = argparse.ArgumentParser(description='Parameter Processing')
    parser.add_argument('--dataset', type=str, default='CIFAR100', help='dataset')
    parser.add_argument('--model', type=str, default='ConvNet', help='model')
    parser.add_argument('--ipc', type=int, default=10, help='image(s) per class')
    parser.add_argument('--epoch_eval_train', type=int, default=1000, help='epochs to train a model with synthetic data')
    parser.add_argument('--lr_net', type=float, default=0.01, help='learning rate for updating network parameters')
    parser.add_argument('--batch_train', type=int, default=256, help='batch size for training networks')
    parser.add_argument('--data_path', type=str, default='./data', help='dataset path')
    parser.add_argument('--num_eval', type=int, default=2, help='the number of evaluating randomly initialized models')

    args = parser.parse_args()
    args.device = 'cuda' if torch.cuda.is_available() else 'cpu'

    if not os.path.exists(args.data_path):
        os.mkdir(args.data_path)

    channel, im_size, num_classes, class_names, mean, std, dst_train, dst_test, testloader = get_dataset(args.dataset, args.data_path)

    model_eval_pool = ['MLP', 'ConvNet', 'LeNet', 'AlexNet', 'VGG11', 'ResNet18']

    saved_data = torch.load('./res_DC_{}_ConvNet_{}ipc.pt'.format(args.dataset, args.ipc))
    image_syn_eval = saved_data['data'][0][0]
    label_syn_eval = saved_data['data'][0][1]

    ''' Evaluate synthetic data '''

    for model_eval in model_eval_pool:
        print('-------------------------\nEvaluation\nmodel_train = %s, model_eval = %s'%(args.model, model_eval))

        args.dc_aug_param = get_daparam(args.dataset, args.model, model_eval, args.ipc) # This augmentation parameter set is only for DC method. It will be muted when args.dsa is True.
        print('DC augmentation parameters: \n', args.dc_aug_param)

        if args.dc_aug_param['strategy'] != 'none':
            args.epoch_eval_train = 1000  # Training with data augmentation needs more epochs.
        else:
            args.epoch_eval_train = 300

        accs = []
        for it_eval in range(args.num_eval):
            net_eval = get_network(model_eval, channel, num_classes, im_size).to(args.device) # get a random model
            _, acc_train, acc_test = evaluate_synset(it_eval, net_eval, image_syn_eval, label_syn_eval, testloader, args)
            accs.append(acc_test)
        print('Evaluate %d random %s, mean = %.4f std = %.4f\n-------------------------'%(len(accs), model_eval, np.mean(accs), np.std(accs)))

if __name__ == '__main__':
    main()


import gym
from openai_ros.task_envs.cartpole_stay_up import stay_up
import rospy
from baselines import deepq

def callback(lcl, _glb):
    # stop training if reward exceeds 199
    is_solved = lcl['t'] > 100 and sum(lcl['episode_rewards'][-101:-1]) / 100 >= 199
    return is_solved

def main():
    rospy.init_node('cartpole_training', anonymous=True, log_level=rospy.WARN)
    env = gym.make("CartPoleStayUp-v0")
    #env = gym.make("CartPole-v0")
    act = deepq.learn(
        env,
        network='mlp',
        lr=1e-3,
        total_timesteps=100000,
        buffer_size=50000,
        exploration_fraction=0.1,
        exploration_final_eps=0.02,
        print_freq=10,
        callback=callback
    )
    print("Saving model to cartpole_model.pkl")
    act.save("cartpole_model.pkl")

if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('api', '0002_auto_20150326_2215'),
    ]

    operations = [
        migrations.AlterField(
            model_name='article',
            name='contentUrl',
            field=models.CharField(unique=True, max_length=1000),
            preserve_default=True,
        ),
        migrations.AlterField(
            model_name='gaguser',
            name='name',
            field=models.CharField(unique=True, max_length=100),
            preserve_default=True,
        ),
        migrations.AlterUniqueTogether(
            name='taguser',
            unique_together=set([('user', 'tag')]),
        ),
        migrations.AlterUniqueTogether(
            name='vote',
            unique_together=set([('article', 'user')]),
        ),
    ]


from matplotlib import pyplot as plt
from wordcloud import WordCloud, STOPWORDS
import matplotlib.colors as mcolors

def display_wordcloud(lda_model):
  cols = [color for name, color in mcolors.TABLEAU_COLORS.items()]  # more colors: 'mcolors.XKCD_COLORS'

  cloud = WordCloud(background_color='white',
                    width=2500,
                    height=1800,
                    max_words=10,
                    colormap='tab10',
                    color_func=lambda *args, **kwargs: cols[i],
                    prefer_horizontal=1.0)

  topics = lda_model.show_topics(formatted=False)

  fig, axes = plt.subplots(3, 2, figsize=(10,10), sharex=True, sharey=True)

  for i, ax in enumerate(axes.flatten()):
      fig.add_subplot(ax)
      topic_words = dict(topics[i][1])
      cloud.generate_from_frequencies(topic_words, max_font_size=300)
      plt.gca().imshow(cloud)
      plt.gca().set_title('Topic ' + str(i), fontdict=dict(size=16))
      plt.gca().axis('off')

  plt.subplots_adjust(wspace=0, hspace=0)
  plt.axis('off')
  plt.margins(x=0, y=0)
  plt.tight_layout()
  plt.show()


from django.urls import path, include
from django.contrib import admin
from drf_spectacular.views import (
    SpectacularAPIView,
    SpectacularSwaggerView,
)
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", include("user.urls")),
    path("api/", include("products.urls")),
    path("api/", include("address.urls")),
    path("api/", include("orders.urls")),
    path("api/", include("cart.urls")),
    path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
    path("api/docs/swagger/", SpectacularSwaggerView.as_view(url_name="schema")),
    path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


from django import forms
from django.core.validators import MinValueValidator, MaxValueValidator
from .models import Item, Exhibition, Artist
from django.db.models.functions import Concat
from django.db.models           import Value, CharField

class ArtistEntryForm(forms.Form):
    CHOICES = [('남성','남성'), ('여성', '여성')]

    username = forms.CharField(label = '이름(16자 이하)', max_length = 16, required = True)
    gender = forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect, label = '성별', required = True)
    birth_date = forms.DateField(label = '생년월일', required = True)
    email = forms.EmailField(label = '이메일', required = True)
    phone_number = forms.CharField(label = '전화번호(하이픈 포함)', max_length=13, required = True)

class ItemEntryForm(forms.Form):
    item_name = forms.CharField(label = '제목 (64자 이하)', max_length=64)
    size = forms.IntegerField(label = '호수 (1~500 사이)', validators=[MinValueValidator(1), MaxValueValidator(500)])

class ExhibitionEntryForm(forms.ModelForm):
    class Meta:
        model = Exhibition
        fields = ('exhibition_name', 'starting_date', 'ending_date')
        labels = {
            "exhibition_name": "제목(64자 이하)",
            "starting_date": "시작 날짜",
            "ending_date" : "종료 날짜"
        }