"""
Author: Matthias Grajewski (grajewski@fh-aachen.de) and Luis Hasenauer
This file is part of faultapprox-python (https://github.com/mgrajewski/faultapprox-python)
"""
from warnings import warn

import numpy as np
import numpy.typing as npt

from scipy.linalg import norm
from typing import Tuple

from checkPoints import component_closed
from computeClassification import compute_classification
from newtriplets import start_pairs, single_triplet_by_bisection
from entities.Entities import FaultApproxParameters, ProblemDescr
from geometry import estimate_curvature_2d, reg_ls

GROWTH_FACTOR = 1.5

def __eval_poly(t, coeffs, q_mat, x_mean):
    ndim = x_mean.shape[0]
    n_points = t.shape[0]

    deg = coeffs.shape[0] - 1

    int_mat = np.zeros((n_points, deg + 1))
    for i in range(deg + 1):
        int_mat[:, i] = t ** i

    x = int_mat @ coeffs

    if ndim == 2:
        x = np.vstack((t, x)).T @ q_mat.T + x_mean
    else:
        x = np.vstack((t, x, np.zeros(n_points, 1))).T @ q_mat + x_mean

    return x

def expand_2d(left_domain: npt.ArrayLike, idx_points: npt.ArrayLike, i_class: int, j_class: int,
              i_seg: int, class_vals: npt.ArrayLike,
              points_i_class: npt.ArrayLike, points_j_class: npt.ArrayLike, normal_vec_plane: npt.ArrayLike,
              problem_descr, fault_approx_pars: FaultApproxParameters, mode: int) -> \
                        Tuple[npt.ArrayLike, npt.ArrayLike, int, npt.ArrayLike, npt.ArrayLike, bool, bool]:
    """
    This function extends a fault line up to its end.

    We assume that the points on the fault line are ordered. There are two modes of operation: mode 0 for extending the
    fault line before its current starting point and mode 1 for extending it beyond its current end.
    Depending on mode, we take the first or last three points (if existing) of the fault line and fit an interpolating
    polynomial curve. We then create new points on this line such that their distance is approximately the distance of
    the ones already existing. These points and the corresponding auxiliary points are used to obtain new points on the
    fault line.
    We repeat this process until we leave the domain, or we intrude in a subdomain neither belonging to i_class nor
    j_class. In this case, we take the first of these points and discard all others of this kind. We adjust its
    parameter by bisection to obtain the maximal parameter where no third class is involved. This point marks
    approximately the end of the fault line.
    For any fault line, we store at which side of the domain the fault line leaves the domain if so in the array
    left_domain. This information is necessary for constructing the approximating polygons of any subdomain in function
    ReconstructSubdomains.

    The information of left_domain is coded as follows:
    1: left boundary,
    2: right boundary,
    3: bottom boundary,
    4: top boundary,
    0: fault line end inside the domain

    This routine is applied for expanding fault surfaces in 3D as well. Here, we take some points from the fault
    surface and treat them as points defining a curve to continue. Again, we need points near the curve on both sides
    of the fault line. Using just a vector normal to the curve as in 2D does not work. Therefore, we need additional
    information which is provided by normal_vec_plane.

    Args:
        left_domain (npt.ArrayLike): See note.
        idx_points (npt.ArrayLike): Array of point indices according to their position on the fault line.
        i_class (int): We deal with the i_segs-th part of the fault line between classes i_class und j_class.
        j_class (int)
        i_seg (int)
        class_vals (npt.ArrayLike):
            Array containing the class values. These are not necessarily the class indices. Imagine that
            f(\Omega) = {1,2,5}. Then, class_vals = [1,2,5], whereas the class indices range from 0 to 2.
            Shape: (nclasses,)
        points_i_class (npt.ArrayLike): The fault points near the current fault line in i_class.
        points_j_class (npt.ArrayLike): The fault points near the current fault line in j_class.
        normal_vec_plane (npt.ArrayLike):
        problem_descr (ProblemDescr):
            Object containing all problem-relevant parameters. We refer to its documentation in Entities.py for details.
        fault_approx_pars (FaultApproxParameters):
            Object containing all parameters relevant for the fault detection algorithm. We refer to its documentation
            in Entities.py for details.
        mode (int): Mode of operation (0 or 1, see description above).

    Returns:
        Tuple[ npt.ArrayLike, npt.ArrayLike, int, npt.ArrayLike, npt.ArrayLike, bool]:
            idx_points_enlarged: array of point indices according to their position on the fault line, but enlarged by
            the indices of the points added
            resort: If the fault line is closed, the start and the end of the fault line in fact overlap. This requires
            a complete resorting of the points. We do not resort inside this routine but indicate the requirement by
            setting resort = true.
            points_added: If really points have been added, this variable is True.

    Raises:
        Exception: If there is an invalid mode in expand_2d.
    """
    num_points_on_curve = 4

    points_added = False

    # maximal number of expansion cycles (to prevent endless loops)
    max_cycles = 500

    finished = False
    bdomain_left = False

    num_points = points_i_class.shape[0]

    if num_points < 2:
        warn("Not enough triplets provided for expanding the fault line.")
        resort = False
        points_added = False
        idx_points_enlarged = idx_points
        n_points = num_points
        return idx_points_enlarged, left_domain, n_points, points_i_class, points_j_class, resort, points_added

    # dist_vec contains actual distance between consecutive points.
    dist_vec_aux = (points_i_class[idx_points[0:num_points - 1], 0] -
                    points_i_class[idx_points[1:num_points], 0]) ** 2 + \
                   (points_i_class[idx_points[0:num_points - 1], 1] - points_i_class[idx_points[1:num_points], 1]) ** 2

    dist_vec_aux = np.sqrt(dist_vec_aux)
    dist_vec = np.zeros(num_points, dtype=float)
    dist_vec[idx_points] = np.concatenate(([dist_vec_aux[0]],
                                           0.5 * (dist_vec_aux[0:num_points - 2] + dist_vec_aux[1:num_points - 1]),
                                           [dist_vec_aux[-1]]))

    # requirement for complete resorting (not necessary by default)
    resort = False

    # for computing valid point pairs as starting values for bisection
    alpha = fault_approx_pars.alpha

    # Points closer than eps are regarded as identical.
    eps = fault_approx_pars.eps

    n_points, ndim = points_i_class.shape

    # auxiliary vector
    aux_vec_loc = np.zeros(3)

    # index array for the extended set points on the surface (at the beginning, this is the original set of points)
    idx_points_enlarged = idx_points

    # number of cycles in expansion
    ncycles = 0

    while not finished and ncycles < max_cycles:

        ncycles = ncycles + 1

        # extend at the beginning
        if mode == 0:
            idx_set = np.arange(min(n_points, num_points_on_curve) - 1, -1, -1)

        # extend at the end
        elif mode == 1:
            idx_set = np.arange(max(0, n_points - num_points_on_curve), n_points)
        else:
            raise Exception("Invalid mode of operation in expand_2d.")

        num_points_to_interpolate = idx_set.shape[0]

        points_on_fault = 0.5 * (points_i_class[idx_points_enlarged[idx_set], :] +
                                 points_j_class[idx_points_enlarged[idx_set], :])

        # extrapolate current points
        new_point_on_fault, avg_dist, extra_par, data_pars, q_mat, x_mean, coeffs = extrapolate_on_fault_line(
            points_on_fault, 1, fault_approx_pars)

        finished_surf_point = False

        i_try = 0

        while i_try <= 3 and not finished_surf_point:

            i_try += 1

            extra_par = 1.0/2**(i_try-1)*(extra_par - data_pars[num_points_to_interpolate - 1]) + \
                data_pars[num_points_to_interpolate - 1]

            new_point_on_fault = __eval_poly(extra_par, coeffs, q_mat, x_mean)

            # distance of the new point relative to the average distance of the existing points
            rel_dist = norm(new_point_on_fault - points_on_fault[num_points_to_interpolate - 1, :], 2, axis=1) / \
                avg_dist

            # logical array indicating which points on the extrapolated fault line are outside the domain
            outside_domain = np.any(np.logical_or(new_point_on_fault < problem_descr.x_min,
                                                  new_point_on_fault > problem_descr.x_max), axis=1)

            # compute normal vector
            aux_vec_loc[0:ndim] = new_point_on_fault - points_on_fault[num_points_to_interpolate - 1, :]

            normal_vec_loc = np.cross(aux_vec_loc, normal_vec_plane)
            normal_vec_loc = normal_vec_loc[0:ndim] / norm(normal_vec_loc, axis=0)

            # this is purely heuristic
            step_size = np.maximum(0.95 * fault_approx_pars.abstol_bisection,
                                   0.5 * np.minimum(alpha * avg_dist,
                                                    3.0 * fault_approx_pars.abstol_bisection * (rel_dist ** 2 + 1)))

            step_size = step_size[:, np.newaxis]

            point_left = new_point_on_fault + step_size * normal_vec_loc
            point_left = np.minimum(np.maximum(problem_descr.x_min + eps, point_left), problem_descr.x_max - eps)

            point_right = new_point_on_fault - step_size * normal_vec_loc
            point_right = np.minimum(np.maximum(problem_descr.x_min + eps, point_right), problem_descr.x_max - eps)

            if not outside_domain:
                aux_arr = compute_classification(np.concatenate((point_left, point_right), axis=0), problem_descr)

                class_point_left = aux_arr[0]
                class_point_right = aux_arr[1]

                outside_sub = (class_point_left != class_vals[i_class] and class_point_left != class_vals[j_class]) or \
                              (class_point_right != class_vals[i_class] and class_point_right != class_vals[j_class])

            else:
                class_point_left = -1
                class_point_right = -1

                outside_sub = False

            # If there are some points on the curve which are neither in i_class nor in j_class, we have reached the
            # boundary of the domain or the subdomain and have therefore reached the last cycle of the expansion
            # loop. As points outside the valid domain have class -1, this includes the case the domain has been left.
            # Note that if the "central" points from which the left and the right points was generated is outside the
            # domain, we automatically assume that this holds for both the left and the right points. Concentrating here
            # on just point_left therefore does not harm.
            if outside_domain:
                finished = True
                bdomain_left = True

                # Approximate the intersection of the extrapolated line and the domain boundary. We know that
                # data_pars[num_points_to_interpolate-1] belongs to a point inside the domain. Therefore, it is a lower
                # bound for the parameter of the intersection. On the other hand, the point corresponding to extra_par
                # does not belong to the domain. Therefore, this is an upper bound for the parameter.
                x_new, iedge = extra_new_point_on_domain_bdry(data_pars[num_points_to_interpolate-1], extra_par,
                                                              points_on_fault[-1, :], new_point_on_fault[0], coeffs,
                                                              q_mat, x_mean, problem_descr, fault_approx_pars)

                # update_vec is not necessarily the normal vector of the extrapolated fault line in x_new, so we call it
                # update_vec.
                update_vec = np.cross(normal_vec_plane, aux_vec_loc)

                # from x_new, we need to create a pair of points as starting point for bisection. We need to ensure that
                # the line segment between these two points is parallel to the appropriate domain boundary. We do this
                # by modifying update_vec.
                if iedge == 1:
                    # x2 is minimal (bottom boundary/front surface of cube)
                    update_vec[1] = 0.0
                elif iedge == 2:
                    # x1 is maximal (right boundary/right surface of cube)
                    update_vec[0] = 0.0
                elif iedge == 3:
                    # x2 is maximal (top boundary/back surface of cube)
                    update_vec[1] = 0.0
                elif iedge == 4:
                    # x1 is minimal (left boundary/left surface of cube)
                    update_vec[0] = 0.0
                elif iedge == 5:
                    # x3 is minimal (bottom surface of cube)
                    update_vec[2] = 0.0
                elif iedge == 6:
                    # x3 is maximal (top surface of cube)
                    update_vec[2] = 0.0

                rel_dist = norm(x_new - points_on_fault[num_points_to_interpolate - 1, :]) / avg_dist
                update_vec = update_vec[0:ndim]

                # heuristic control of step length
                update_vec = update_vec / norm(update_vec) * np.max((0.95 * fault_approx_pars.abstol_bisection,
                                                                     3.0 * fault_approx_pars.abstol_bisection *
                                                                     (rel_dist ** 2 + 1.0)))

                x_new_left = x_new + update_vec
                x_new_right = x_new - update_vec

                # Ensure that the new points near the boundary are really inside the domain.
                x_new_left = np.maximum(np.minimum(x_new_left, problem_descr.x_max - eps), problem_descr.x_min + eps)
                x_new_right = np.maximum(np.minimum(x_new_right, problem_descr.x_max - eps), problem_descr.x_min + eps)

                [class_left, class_right] = compute_classification(np.vstack((x_new_left, x_new_right)), problem_descr)
                left_domain[i_class, j_class, i_seg] = iedge

                # Find starting values for bisection.
                point_pairs, idx_ok, class_points_ok = start_pairs(x_new_left.reshape((-1, ndim)),
                                                                   x_new_right.reshape((-1, ndim)),
                                                                   np.array([class_left]), np.array([class_right]),
                                                                   class_vals[i_class], class_vals[j_class],
                                                                   problem_descr)
                class_points_ok = class_points_ok[0]

                if idx_ok:
                    point_left, point_right, finished_surf_point = single_triplet_by_bisection(point_pairs[0, 0:ndim],
                                                                                point_pairs[0,
                                                                                ndim:2 * ndim],
                                                                                class_points_ok[0],
                                                                                class_points_ok[1],
                                                                                problem_descr,
                                                                                fault_approx_pars)

                left_domain[i_class, j_class, i_seg] = iedge

            # There is no indication that the fault line has left the domain. Therefore, we can continue to expand
            # unless it ends inside the domain.
            elif outside_sub:
                finished = True
                bdomain_left = False

                # Did we expand the fault line beyond its end? If so, either point_left or point_right belongs neither
                # to i_class nor to j_class.
                # In this case, take the last known point in the subdomain and the first known outside, discard all
                # others and find the end of the extrapolated fault line by bisection.
                # However, this is a heuristic approach, as it may happen that e.g. due to insufficient distance to the
                # extrapolation curve, both points belong to the same class.
                #                 %                      II
                #                 %        -------------------------->     x point_left
                #                 % ______x________x________x_____         x extrapolated point
                #                 %                               \--__    x point_right
                #                 %              I                 \   ---__
                #                 %                                 \       ---__
                #                 %                                  \    III    ---__
                #
                # Then, we do not detect that we extrapolated the fault line beyond its end. We will fail to find a
                # valid starting pair such that prolongation stops and no point is added, even if the end of the fault
                # line is not yet reached.

                # A fault line ends inside the domain by intersecting a subdomain with a third class.
                # We aim at finding the intersection of the extrapolated fault line with that third subdomain using
                # bisection.

                # A lower bound t_min for the parameter of the intersection is the parameter of the last known
                # point inside the domain.
                t_min = data_pars[num_points_to_interpolate - 1]

                # Correspondingly, the parameter of the first point on the extrapolated fault line which is known
                # to be outside the subdomain is an upper bound for the parameter of the intersection.
                t_max = extra_par[0]

                # This point is known to be inside the subdomain.
                x_min = __eval_poly(np.array([t_min]), coeffs, q_mat, x_mean)[0]
                x_max = __eval_poly(np.array([t_max]), coeffs, q_mat, x_mean)[0]
                dist = norm(x_max - x_min)

                x_min_aux_all = np.empty((0, 2*ndim))
                x_min_all = np.empty((0, ndim))
                class_min_all = np.empty((0, ndim), dtype=int)

                iiter = 1
                while iiter < 10 and dist > fault_approx_pars.abstol_bisection:

                    t_new = 0.5 * (t_min + t_max)

                    x_new = __eval_poly(np.array([t_new]), coeffs, q_mat, x_mean)[0]

                    aux_vec_loc[0:ndim] = x_new - x_min
                    normal_vec_loc = np.cross(aux_vec_loc, normal_vec_plane)
                    normal_vec_loc = normal_vec_loc[0:ndim]/np.linalg.norm(normal_vec_loc, 2)
                    rel_dist = norm(points_on_fault[num_points_to_interpolate-1, :] - x_new, 2)/avg_dist

                    # this is purely heuristic
                    step_size = max(0.95 * fault_approx_pars.abstol_bisection, 0.5 *
                                    min(alpha * avg_dist,
                                        3.0 * fault_approx_pars.abstol_bisection * (rel_dist ** 2 + 1)))

                    x_new_left = x_new + step_size*normal_vec_loc
                    x_new_right = x_new - step_size*normal_vec_loc

                    x_new_left = np.maximum(np.minimum(x_new_left, problem_descr.x_max - eps),
                                            problem_descr.x_min + eps)
                    x_new_right = np.maximum(np.minimum(x_new_right, problem_descr.x_max - eps),
                                             problem_descr.x_min + eps)

                    class_new = compute_classification(np.vstack((x_new_left, x_new_right)), problem_descr)

                    if np.any(np.logical_and(class_new != class_vals[i_class], class_new != class_vals[j_class])):
                        t_max = t_new
                        x_max = x_new
                    else:
                        t_min = t_new
                        x_min = x_new
                        x_min_aux_all = np.vstack((x_min_aux_all, np.hstack((x_new_left, x_new_right))))
                        x_min_all = np.vstack((x_min_all, x_min))
                        class_min_all = np.vstack((class_min_all, class_new))

                    dist = norm(x_max - x_min)

                    iiter += 1

                iiter = 0
                finished_surf_point = False

                while iiter < x_min_all.shape[0] and not finished_surf_point:

                    iiter += 1
                    x_new = x_min_all[-iiter]

                    # We need a point which is inside the current subdomain, and we know that x_min is inside the
                    # domain.
                    # However, if t_min is (almost) at its initial value, x_min is in fact the last known point on the
                    # fault. It does not make sense to add this point again.
                    if norm(x_new - points_on_fault[num_points_to_interpolate-1]) > \
                            fault_approx_pars.abstol_bisection:

                        x_new_left = x_min_aux_all[-1, 0:ndim]
                        x_new_right = x_min_aux_all[-1, ndim: 2*ndim]

                        class_new[0] = compute_classification(x_new, problem_descr)[0]

                        x_new_suitable = class_new[0] == class_vals[i_class] or class_new[0] == class_vals[j_class]

                        if x_new_suitable:
                            # x_new belongs to a valid point pair
                            if class_new[0] == class_vals[i_class] and class_min_all[-iiter, 0] == class_vals[j_class]:
                                x_new_left = x_new
                                class_point_left = class_new[0]
                                x_new_right = x_min_aux_all[-iiter, 0:ndim]
                                class_point_right = class_min_all[-iiter, 0]
                            elif class_new[0] == class_vals[j_class] and class_min_all[-iiter, 0] == \
                                    class_vals[i_class]:
                                x_new_left = x_new
                                class_point_left = class_new[0]
                                x_new_right = x_min_aux_all[-iiter, 0:ndim]
                                class_point_right = class_min_all[-iiter, 0]
                            elif class_new[0] == class_vals[j_class] and class_min_all[-iiter, 1] == \
                                    class_vals[i_class]:
                                x_new_left = x_new
                                class_point_left = class_new[0]
                                x_new_right = x_min_aux_all[-iiter, ndim:2*ndim]
                                class_point_right = class_min_all[-iiter, 1]
                            elif class_new[0] == class_vals[i_class] and class_min_all[-iiter, 1] == \
                                    class_vals[j_class]:
                                x_new_left = x_new
                                class_point_left = class_new[0]
                                x_new_right = x_min_aux_all[-iiter, ndim:2*ndim]
                                class_point_right = class_min_all[-iiter, 1]

                            point_left, point_right, finished_surf_point = single_triplet_by_bisection(
                                x_new_left, x_new_right, class_point_left, class_point_right, problem_descr,
                                fault_approx_pars)
                            class_points_ok = np.array([class_point_left, class_point_right], dtype=int)

                left_domain[i_class, j_class, i_seg] = 0

            # expansion continues
            else:

                # Find starting values for bisection.
                point_pairs, idx_ok, class_points_ok = start_pairs(point_left,
                                                                   point_right,
                                                                   np.array([class_point_left]),
                                                                   np.array([class_point_right]),
                                                                   class_vals[i_class],
                                                                   class_vals[j_class],
                                                                   problem_descr)

                class_points_ok = class_points_ok[0]

                # If no point has been found at all, stop. If we have no indication that the fault line left the domain,
                # we must assume that it ended inside.
                if idx_ok:
                    point_left, point_right, finished_surf_point = single_triplet_by_bisection(point_pairs[0, 0:ndim],
                                                                                point_pairs[0,
                                                                                ndim:2 * ndim],
                                                                                class_points_ok[0],
                                                                                class_points_ok[1],
                                                                                problem_descr,
                                                                                fault_approx_pars)
                else:
                    left_domain[i_class, j_class, i_seg] = 0
                    finished = True

            # Add the new point if appropriate.
            if finished_surf_point:

                # Compute the distance to the currently last point on the fault line.
                # If this distance is tiny, replace this point by the new one.
                last_current_point = points_on_fault[-1, :]
                if class_points_ok[0] == class_vals[i_class]:
                    dist_to_new_point = norm(last_current_point - point_left)
                else:
                    dist_to_new_point = norm(last_current_point - point_right)

                # The new point is sufficiently far away from the last known one: add it.
                if dist_to_new_point > avg_dist * fault_approx_pars.min_dist_factor:
                    i_idx_add = n_points
                    n_points += 1

                    if mode == 0:
                        idx_points_enlarged = np.hstack((n_points-1, idx_points_enlarged))
                    elif mode == 1:
                        idx_points_enlarged = np.hstack((idx_points_enlarged, n_points-1))

                # The new point is almost the last known one: replace the last known one by the new one and stop
                # expanding.
                else:
                    i_idx_add = idx_points_enlarged[idx_set[-1]]
                    finished = True

                if class_points_ok[0] == class_vals[i_class]:
                    if i_idx_add == points_i_class.shape[0]:
                        points_i_class = np.concatenate((points_i_class, point_left.reshape(1, -1)), axis=0)
                        points_j_class = np.concatenate((points_j_class, point_right.reshape(1, -1)), axis=0)
                    else:
                        points_i_class[i_idx_add] = point_left
                        points_j_class[i_idx_add] = point_right
                else:
                    if i_idx_add == points_i_class.shape[0]:
                        points_i_class = np.concatenate((points_i_class, point_right.reshape(1, -1)), axis=0)
                        points_j_class = np.concatenate((points_j_class, point_left.reshape(1, -1)), axis=0)
                    else:
                        points_i_class[i_idx_add] = point_right
                        points_j_class[i_idx_add] = point_left

                # If the fault line appears to start or end inside the domain, it may be closed. We test this here.
                if left_domain[i_class, j_class, i_seg] <= 0:
                    closed_comp = component_closed(points_i_class, idx_points_enlarged, dist_vec, n_points, mode)

                    if closed_comp:
                        # If the fault component is closed, it does not start or end at the domain boundary. We finish
                        # expanding this fault line and state that it starts and ends inside the domain.
                        left_domain[i_class, j_class, i_seg] = 0

                        # The new point is in fact somewhere between existing points. This requires resorting.
                        resort = True
                        finished = True

                points_added = True
                if i_idx_add < dist_vec.shape[0]:
                    dist_vec[i_idx_add] = dist_to_new_point
                else:
                    dist_vec = np.hstack((dist_vec, dist_to_new_point))

            # No suitable point for adding found: try again. We assume in this case that the fault line starts or ends
            # inside the domain unless found otherwise.
            else:
                if left_domain[i_class, j_class, i_seg] < 0 and not bdomain_left:
                    left_domain[i_class, j_class, i_seg] = 0

    return idx_points_enlarged, left_domain, n_points, points_i_class, points_j_class, resort, points_added

def extrapolate_on_fault_line(points_on_curve: npt.ArrayLike, num_points_new: int,
                              fault_approx_pars: FaultApproxParameters) -> Tuple[npt.ArrayLike, float,
                                                                                npt.ArrayLike, npt.ArrayLike,
                                                                                npt.ArrayLike, npt.ArrayLike,
                                                                                npt.ArrayLike]:
    """This function computes num_points_new points on an extrapolated fault line. It is constructed using the points
    given in points_on_curve and fitting a polynomial in local coordinates. As the points are not known exactly, we do
    not interpolate exactly, but penalise the second derivative in a least-squares approximation. Following Morozov, we
    choose the regularisation such that the maximal residual is approx. fault_approx_pars.abstol_bisection.
    These points are constructed such that their distance is the minimum of GROW_FACTOR*the average distance of the
    existing points, fault_approx_pars.max_dist_for_surface_points and the requirement that the deviation from a
    straight line is at most (approximately) fault_approx_pars.err_max.

    Args:
        points_on_curve (npt.ArrayLike):
            Points on the curve representing a fault line (must be ordered). Shape: (npoints, ndim)
        num_points_new (int):
            Number of new points on the curve aka fault line constructed by extrapolation.
        fault_approx_pars (FaultApproxParameters):
            Class containing all relevant parameters for our method of detecting fault lines.

    Returns:
        new_points_on_curve (npt.ArrayLike):
            cartesian coordinates of the new points. Shape: (number of new points, ndim)
        avg_dist (float):
            average distance of the points on the fault line used for extrapolation
        extra_pars (npt.ArrayLike):
            Parameters aka local coordinates of the new points on the polynomial curve.
        data_pars (npt.ArrayLike):
            Parameters aka local coordinates of the points in points_on_curve.
        q_mat (npt.ArrayLike):
            orthogonal matrix; the local coordinates are computed by xloc = q_mat@x + x_mean
        x_mean (npt.ArrayLike):
            origin of the local coordinate system; the local coordinates are computed by xloc = q_mat@x + x_mean
        coeffs (npt.ArrayLike):
            coefficients of the extrapolating polynomial
    """

    num_points_on_curve, ndim = points_on_curve.shape

    # we need at least two points for extrapolation
    if num_points_on_curve < 2:
        warn('Too few points to extrapolate')
        new_points_on_curve = np.array([0, 2])
        avg_dist = 0.0
        data_pars = np.array([])
        extra_pars = np.array([])
        q_mat = np.zeros((ndim, ndim))
        x_mean = np.zeros(0)
        coeffs = np.array([])
        return new_points_on_curve, avg_dist, extra_pars, data_pars, q_mat, x_mean, coeffs

    # average distance of the points on the fault line used for extrapolation
    avg_dist = norm(points_on_curve[num_points_on_curve - 1] - points_on_curve[0], 2) / (num_points_on_curve - 1)

    x_mean = (1.0 / num_points_on_curve) * np.ones(num_points_on_curve).dot(points_on_curve)

    points_shifted = points_on_curve - x_mean

    q_mat = np.linalg.svd(points_shifted)[2].T
    points_rot = points_shifted @ q_mat

    # We limit the step size for extrapolation based upon curvature. It does not make sense to extrapolate very far
    # with a lot of numerical amount and to adaptively refine in between the current and the new points anyway
    # afterwards. To do so, we compute the step size which would lead to the maximal admissible deviation
    # fault_approx_pars.err_max from a straight line segment. This is a natural upper bound for the step size in
    # extrapolation.
    #
    # The maximal deviation d of a curve with curvature c from an interpolating line segment with length l is
    #   d = 1/4 c l^2 + 1/16 c^3 l^4 + h.o.t
    # Rearranging leads to l = 2/c^2(sqrt{1+4cd} - 1), which is numerically unstable if cd is small. We set (cl)^2 = v
    # and search for the bigger root of the quadratic equation
    #   v^2 + 4 v - 16cd = 0, aka v^2 + 2pv - q = 0.
    # According to Vieta, q = vmin*vmax with the two roots vmin and vmax. Therefore,
    #   vmin = - (2 + sqrt{4+ 16cd}) = -2 (1 + sqrt{1 + 4cd}),
    # which is stable to evaluate and vmax = -16cd/vmin, and ultimately lmax = sqrt{vmax}/c. Inserting vmax yields
    #   lmax = sqrt{-16cd/vmin}/c = 4 sqrt{d/(-c vmin)}

    # Estimating curvature is possible only for more than two points on the fault line and two dimensions.
    if num_points_on_curve > 2 and ndim == 2:
        curv = estimate_curvature_2d(points_shifted, num_points_on_curve - 1,
                                     fault_approx_pars.abstol_bisection)[0] + \
               estimate_curvature_2d(points_shifted, num_points_on_curve - 2, fault_approx_pars.abstol_bisection)[0]

        if curv > 1e-10:
            lmax = 2.0 * (1 + np.sqrt(1.0 + 4 * curv * fault_approx_pars.err_max))
            lmax = 4.0 * np.sqrt(fault_approx_pars.err_max / (curv * lmax))
        else:
            lmax = 1e10

    # Limiting step size based upon curvature does not work, if only two points on the fault line are known.
    else:
        lmax = 1e10

    step_size = min([fault_approx_pars.max_dist_for_surface_points, GROWTH_FACTOR * avg_dist, lmax])

    if np.all(points_rot[0:num_points_on_curve - 1, 0] < points_rot[1:num_points_on_curve, 0]):
        extra_pars = points_rot[num_points_on_curve - 1, 0] + step_size * np.arange(1, num_points_new + 1)
    elif np.all(points_rot[0:num_points_on_curve - 1, 0] > points_rot[1:num_points_on_curve, 0]):
        extra_pars = points_rot[num_points_on_curve - 1, 0] - step_size * np.arange(1, num_points_new + 1)

    # It is not possible to extrapolate the fault line by any function respecting the order of the points. This is
    # usually the case if the points are on a curve with very strong curvature. Therefore, we just take the two last
    # points and perform linear extrapolation.
    else:
        num_points_on_curve = 2
        if points_rot[num_points_on_curve - 2, 0] < points_rot[num_points_on_curve - 1, 0]:
            extra_pars = points_rot[num_points_on_curve - 1, 0] + step_size * np.arange(1, num_points_new + 1)
        else:
            extra_pars = points_rot[num_points_on_curve - 1, 0] - step_size * np.arange(1, num_points_new + 1)

    int_mat = np.zeros((num_points_on_curve, num_points_on_curve))
    penalty_mat = np.zeros((num_points_on_curve, num_points_on_curve))
    for i in range(num_points_on_curve):
        int_mat[:, i] = points_rot[0: num_points_on_curve, 0] ** i
        penalty_mat[:, i] = i * (i - 1) * points_rot[0: num_points_on_curve, 0] ** (np.max((0, i - 2)))

    # Regularization by penalizing the second derivative is pointless if only two points exist so far: Then, we perform
    # linear extrapolation, and its second derivative is zero anyway.
    if num_points_on_curve > 2:
        penalty_mat = penalty_mat.T @ penalty_mat
        coeffs = reg_ls(int_mat, penalty_mat, points_rot[:, 1], fault_approx_pars.abstol_bisection, 2)
    else:
        coeffs = np.linalg.solve(int_mat, points_rot[0:num_points_on_curve, 1])

    new_points_on_curve = __eval_poly(extra_pars, coeffs, q_mat, x_mean)
    data_pars = points_rot[:, 0]

    # It may happen that the true distance of the extrapolated points to the existing ones is too large, as the arc
    # length on the curve is much greater than the distance of the parameters. In this case, we adjust the parameters
    # accordingly. For the sake of simplicity, we consider the true distance of the last existing to the first new
    # point only.
    true_dist = np.linalg.norm(new_points_on_curve - points_on_curve[num_points_on_curve - 1, :], axis=1)

    if true_dist[0] > 1.1 * fault_approx_pars.max_dist_for_surface_points:
        tmax = extra_pars[0]
        tmin = data_pars[num_points_on_curve - 1]

        iiter = 1

        # The maximal number of iterations should never be reached in practical examples. If so, the adjusted values are
        # at least better than the original ones.
        tnew = 0.0
        while iiter < 20:
            tnew = 0.5 * (tmin + tmax)
            xnew = __eval_poly(np.array([tnew]), coeffs, q_mat, x_mean)

            dist = norm(xnew - points_on_curve[num_points_on_curve - 1, :])
            res = dist - fault_approx_pars.max_dist_for_surface_points

            # if the distance to the existing points is max_dist_for_surface_points up to 10%
            if np.abs(res) < 0.1 * fault_approx_pars.max_dist_for_surface_points:
                break
            else:
                if dist > fault_approx_pars.max_dist_for_surface_points:
                    tmax = tnew
                else:
                    tmin = tnew

            iiter = iiter + 1

        extra_pars = data_pars[num_points_on_curve - 1] + \
            np.arange(1, num_points_new + 1) * (tnew - data_pars[num_points_on_curve - 1])
        new_points_on_curve = __eval_poly(extra_pars, coeffs, q_mat, x_mean)

    return new_points_on_curve, avg_dist, extra_pars, data_pars, q_mat, x_mean, coeffs

def extra_new_point_on_domain_bdry(t_min: float, t_max: float, x_min: npt.ArrayLike, x_max: npt.ArrayLike,
                                   coeffs: npt.ArrayLike, q_mat: npt.ArrayLike, x_mean: npt.ArrayLike,
                                   problem_descr: ProblemDescr, fault_approx_pars: FaultApproxParameters) ->\
        Tuple[npt.ArrayLike, int]:
    """
    This function aims at finding the intersection of an extrapolated fault line with the domain boundary using
    bisection. We exploit that the domain is either a rectangle or a cuboid.
    We assume that the fault line has been extrapolated with a polynomial in local coordinates. We approximate the
    parameter value t of the intersection of that polynomial with the domain boundary by bisection.
    For doing so, we need a lower and upper bound t_min and t_max for that parameter t along with the corresponding
    points x_min and x_max.

    Args:
        t_min (float):
            lower bound for the parameter value of the intersection
        t_max (float):
            upper bound for the parameter value of the intersection
        x_min (npt.ArrayLike):
            point on the extrapolating polynomial corresponding to t_min given in global coordinates
        x_max (npt.ArrayLike):
            point on the extrapolating polynomial corresponding to t_max given in global coordinates
        coeffs (npt.ArrayLike):
            coefficients of the extrapolating polynomial
        q_mat (npt.ArrayLike):
            orthogonal matrix describing the local coordinate system
        x_mean (npt.ArrayLike):
            origin of the local coordinate system
        problem_descr (ProblemDescr):
            Object containing all problem-relevant parameters. We refer to its documentation in Entities.py for details.
        fault_approx_pars (FaultApproxParameters):
            Object containing all parameters relevant for the fault detection algorithm. We refer to its documentation
            in Entities.py for details.

    Returns:
        x_new (npt.ArrayLike):
            approximate intersection of fault line and domain boundary
        iedge (int):
            index of the domain boundary part, at which the fault line leaves the domain
    """

    # indices of the domain edges (in 2D):
    #        y /\
    #          │     3
    #          ├───────────┐
    #          │           │
    #        4 │           │ 2
    #          │           │
    #          └───────────│─────> x
    #                1
    #
    # indices of the domain surfaces (in 3D)
    #                   6          3              z
    #                   ┊         ╱               △      y
    #               ┌───┊───────╱───┐             │     ╱
    #             ╱ │   ┊     ╱   ╱ │             │   ╱
    #           ╱   │   v   ↙   ╱   │             │ ╱
    #         ╱     │         ╱     │             └─────────> x
    #        ┌───────────────┐      │
    #  4┈┈┈┈┈│┈>    │        │    <┈┈┈┈┈┈┈2
    #        │      └--------│------┘
    #        │     ╱         │     ╱
    #        │   ╱   ↗  △    │   ╱
    #        │ ╱   ╱    ┊    │ ╱
    #        └───╱──────┊────┘
    #          ╱        ┊
    #         1         5

    i_aux_min = np.array([4, 1, 5], dtype=int)
    i_aux_max = np.array([2, 3, 6], dtype=int)

    # Points closer than eps are considered identical.
    eps = fault_approx_pars.eps

    ndim = x_min.shape[0]

    # euclidean distance of these points
    dist = norm(x_max - x_min)
    iiter = 1

    while iiter < 10:

        # The factor 0.5 in the following condition is just a safety factor (iterating one time more does not increase
        # the number of function evaluations).
        if dist < 0.5 * fault_approx_pars.abstol_bisection:
            break
        else:
            t_new = 0.5 * (t_min + t_max)
            x_new = __eval_poly(np.array([t_new]), coeffs, q_mat, x_mean)[0]

            if np.any(np.logical_or(x_new < problem_descr.x_min, x_new > problem_descr.x_max)):
                t_max = t_new
                x_max = x_new
            else:
                t_min = t_new
                x_min = x_new

            # The distance will approximately halve per iterations step, but only approximately (the
            # extrapolation is not necessarily a straight line).
            dist = norm(x_max - x_min)

        iiter = iiter + 1

    # index of the edge or facet at which the fault line leaves the domain
    iedge = 0

    for i in range(0, ndim):
        if x_max[i] > problem_descr.x_max[i]:
            iedge = i_aux_max[i]
        elif x_max[i] < problem_descr.x_min[i]:
            iedge = i_aux_min[i]

    # The approximation of the intersection is not necessarily inside the domain, so we enforce this here.
    x_new = np.maximum(np.minimum(x_new, problem_descr.x_max - eps), problem_descr.x_min + eps)

    # Moreover, ensure that the new point is up to eps on the domain boundary (it is somewhere inside near a boundary
    # up to abstol_bisection, but we can do better).
    if iedge == 1:
        # x2 is minimal (bottom boundary/front surface of cube)
        x_new[1] = problem_descr.x_min[1] + eps
    elif iedge == 2:
        # x1 is maximal (right boundary/right surface of cube)
        x_new[0] = problem_descr.x_max[0] - eps
    elif iedge == 3:
        # x2 is maximal (top boundary/back surface of cube)
        x_new[1] = problem_descr.x_max[1] - eps
    elif iedge == 4:
        # x1 is minimal (left boundary/left surface of cube)
        x_new[0] = problem_descr.x_min[0] + eps
    elif iedge == 5:
        # x3 is minimal (bottom surface of cube)
        x_new[2] = problem_descr.x_min[1] + eps
    elif iedge == 6:
        # x3 is maximal (top surface of cube)
        x_new[2] = problem_descr.x_max[0] - eps

    return x_new, iedge

def test2d():
    """Testfunction 2d.

    """
    from tests.test_funcs.TestFunc2D import func_fd_2d_cl3_c0_01
    problem_descr = ProblemDescr()
    problem_descr.test_func = func_fd_2d_cl3_c0_01
    problem_descr.x_min = np.array([0, 0])
    problem_descr.x_max = np.array([1, 1])
    fault_approx_pars = FaultApproxParameters()
    fault_approx_pars.max_dist_for_surface_points = 0.05
    fault_approx_pars.abstol_bisection = 0.001
    fault_approx_pars.num_points_local = 10

    points_on_curve = np.array([[0, 0], [1, 1], [2, 4]])
    num_points_new = 2
    new_points_on_curve, avg_dist, extra_pars, data_pars, q_mat, x_mean, coeffs = \
        extrapolate_on_fault_line(points_on_curve, num_points_new, fault_approx_pars)

    print(new_points_on_curve)

def test3d():
    """Testfunction 3d.
    """
    from tests.test_funcs.TestFunc2D import func_fd_2d_cl2_c1_03
    left_domain = np.zeros((2, 2), dtype=int)
    idx_points_surf_ordered = np.array([1, 2]) - 1
    i_class = 1
    j_class = 2
    i_seg = 1
    class_vals = np.array([1, 2])
    points_i_class = np.array([[0.028, 0.295, 0.336], [0.001, 0.295, 0.337]])
    points_j_class = np.array([[0.028, 0.295, 0.335], [0, 0.295, 0.336]])
    normal_vec_plane = np.array([-1, 0, 0])
    problem_descr = ProblemDescr()
    problem_descr.test_func = func_fd_2d_cl2_c1_03
    problem_descr.x_min = np.array([0, 0, 0])
    problem_descr.x_max = np.array([1, 1, 1])
    fault_approx_pars = FaultApproxParameters()
    fault_approx_pars.max_dist_for_surface_points = 0.05
    fault_approx_pars.num_points_local = 5
    fault_approx_pars.abstol_bisection = 0.001
    mode = 1
    idx_points_surf_ordered_enlarged, left_domain, n_points, points_i_class, points_j_class, resort, points_added =\
        expand_2d(left_domain, idx_points_surf_ordered, i_class, j_class, i_seg, class_vals,
                  points_i_class, points_j_class, normal_vec_plane, problem_descr, fault_approx_pars,
                  mode)
    print(idx_points_surf_ordered_enlarged, left_domain, n_points, points_i_class, points_j_class, resort, points_added,
          sep="\n\n")

if __name__ == '__main__':
    test2d()


import os
from glob import glob
from typing import Optional
import albumentations as albu
import torch
import yaml
from fire import Fire
from tqdm import tqdm
from matplotlib import pyplot as plt
from aug import get_normalize
from models.networks import get_poolnet

import torch

import numpy as np
import os
import torchvision.utils as vutils
import cv2
import math
import time

class Predictor:
    def __init__(self, weights_path: str, model_name: str = ''):
        with open('config/config.yaml') as cfg:
            config = yaml.load(cfg)
        model = get_poolnet()
        model.load_state_dict(torch.load(weights_path)['model'])
        self.model = model.cuda()
        self.model.train(True)
        # GAN inference should be in train mode to use actual stats in norm layers,
        # it's not a bug
        self.normalize_fn = get_normalize()

    @staticmethod
    def _array_to_batch(x):
        x = np.transpose(x, (2, 0, 1))
        x = np.expand_dims(x, 0)
        return torch.from_numpy(x)

    def _preprocess(self, x: np.ndarray, mask: Optional[np.ndarray]):
        x, _ = self.normalize_fn(x, x)
        if mask is None:
            mask = np.ones_like(x, dtype=np.float32)
        else:
            mask = np.round(mask.astype('float32') / 255)

        h, w, _ = x.shape
        block_size = 32
        min_height = (h // block_size + 1) * block_size
        min_width = (w // block_size + 1) * block_size

        pad_params = {'mode': 'constant',
                      'constant_values': 0,
                      'pad_width': ((0, min_height - h), (0, min_width - w), (0, 0))
                      }
        x = np.pad(x, **pad_params)
        mask = np.pad(mask, **pad_params)

        return map(self._array_to_batch, (x, mask)), h, w

    @staticmethod
    def _postprocess(x: torch.Tensor) -> np.ndarray:
        x, = x
        x = x.detach().cpu().float().numpy()
        x = (np.transpose(x, (1, 2, 0)) + 1) / 2.0 * 255.0
        return x.astype('uint8')

    def __call__(self, img: np.ndarray, mask: Optional[np.ndarray], ignore_mask=True) -> np.ndarray:
        (img, mask), h, w = self._preprocess(img, mask)
        with torch.no_grad():
            inputs = [img.cuda()]
            if not ignore_mask:
                inputs += [mask]
            pred = self.model(*inputs)
        return self._postprocess(pred)[:h, :w, :]

def main(

         weights_path='godbless_fpn.h5',
         out_dir='submit/',outdir2='result/',
         ):
    def sorted_glob(pattern):
        return sorted(glob(pattern))

    # for root, _ , _ in os.walk('/home/lym/sci/testpic100/blur/'):
    for root, _, _ in os.walk('/home/lym/sci/pictest200/GROPO_dataset/blur/'):
        if root == '/home/lym/sci/pictest200/GROPO_dataset/blur/':
            continue
        print(root)
        a = root.split('/')
        print(a)
        out_dir = 'submit/'+a[-1]
        out_dir2 = '720sharp/'+a[-1]
        # imgs = sorted_glob('/home/lym/sci/testpic100/blur/' + a[-1] + '/*.png')
        # masks = sorted_glob('/home/lym/sci/testpic100/sharp/'+ a[-1]+'/*.png')##### 做第二次去模糊 只需要改动这里

        imgs = sorted_glob('/home/lym/sci/pictest200/GROPO_dataset/blur/' + a[-1] + '/*.png')  ####  更改自己的路径
        masks = sorted_glob('/home/lym/sci/pictest200/GROPO_dataset/sharp/' + a[-1] + '/*.png')  ### if '/home/lym/sci/pictest/sharp/*/*.png' is not None else [None for _ in imgs]

        pairs = zip(imgs, masks)
        # names = sorted([os.path.basename(x) for x in glob('/home/lym/sci/testpic100/blur/'+a[-1]+'/*.png')])
        names = sorted([os.path.basename(x) for x in glob('/home/lym/sci/pictest200/GROPO_dataset/blur/' + a[-1] + '/*.png')])
        predictor = Predictor(weights_path=weights_path)
        nums = len(imgs)
        os.makedirs(out_dir, exist_ok=True)
        os.makedirs(out_dir2, exist_ok=True)
        time_s = time.time()
        for name, pair in tqdm(zip(names, pairs), total=len(names)):
            f_img, f_mask = pair
            img, mask = map(cv2.imread, (f_img, f_mask))

            aug =  albu.CenterCrop(720, 720, always_apply=True)
            img = aug(image=img)["image"]
            mask = aug(image=mask)["image"]
            # plt.subplot(1,2,1)
            # plt.imshow(img)
            # plt.subplot(1,2,2)
            # plt.imshow(mask)
            # plt.show()

            col=img.shape[1]/2
            row=img.shape[0]/2
            #img =  cv2.resize(img, (int(col),int(row)), interpolation=cv2.INTER_CUBIC)
            image = predictor(img, mask)
            ####如果您想储存下来结果  此处只是测速
            #image = np.concatenate([img, image,mask], axis=1)
            #image2 = np.concatenate([image])
            cv2.imwrite(os.path.join(out_dir,name), image)
            cv2.imwrite(os.path.join(out_dir2, name), mask)

        time_k = time.time()
        ####我测试了200张  平均速度0.373

        print('Speed: %f FPS' % ((time_k-time_s)/nums))
        print('Test Done!')

if __name__ == '__main__':
    Fire(main)


import numpy as np
import matplotlib.pylab as plt
from scipy.stats import ttest_ind, mannwhitneyu

def Cohen_d(a,b):
    s1 = np.std(a, ddof=1)**2
    s2 = np.std(b, ddof=1)**2
    return (a.mean() - b.mean()) / np.sqrt(0.5*(s1+s2))

#  Load the MCC for repeated trainings
m_no = np.load("fmnist_no_momentum_runs.npy")
m_w = np.load("fmnist_w_momentum_runs.npy")

hn,xn = np.histogram(m_no, bins=5)
hw,xw = np.histogram(m_w, bins=5)
b = plt.bar(xn[:-1], hn, width=0.8*(xn[1]-xn[0]), hatch="/", color="#5f5f5f")
b = plt.bar(xw[:-1], hw, width=0.8*(xn[1]-xn[0]), hatch="\\", color="#7f7f7f")
plt.xlabel("MCC")
plt.ylabel("Count")
plt.tight_layout(pad=0, w_pad=0, h_pad=0)
plt.savefig("fmnist_mcc_plot.png", dpi=300)
plt.show()

print()
print("no momentum: %0.5f +/- %0.5f" % (m_no.mean(), m_no.std(ddof=1)/np.sqrt(len(m_no))))
print("momentum   : %0.5f +/- %0.5f" % (m_w.mean(), m_w.std(ddof=1)/np.sqrt(len(m_w))))
print()
t,p = ttest_ind(m_w, m_no)
print("t-test momentum vs no (t,p): (%0.8f, %0.8f)" % (t,p))
U,p = mannwhitneyu(m_w, m_no)
print("Mann-Whitney U             : (%0.8f, %0.8f)" % (U,p))
print("Cohen's d                  : %0.5f" % Cohen_d(m_w, m_no))
print()


from fastapi import HTTPException,status
import requests
from config import config
import zipfile
import io
import os

async def zipfileCustomRulesValidator(file,fields):
    try:
        zip_content = await file.read()
        # Create a BytesIO object to work with the ZIP content
        zip_buffer = io.BytesIO(zip_content)

        # Extract the contents of the ZIP file
        with zipfile.ZipFile(zip_buffer, "r") as zip_ref:
            # List the contents of the ZIP file
            file_list = zip_ref.namelist()
            root_folder = None
            subfolders = set()
            zip_ref.close()
            # print(file_list)

        ## Write your conditions below

        ## Getting a Root folder and sub folder

        for file_name in file_list:
            parts = file_name.split("/")
            if "node_modules" in parts:
                return {"code":400,"message":"Please Upload without node_modules"}

            if len(parts) >= 1:
                if root_folder is None:
                    root_folder = parts[0]
                elif root_folder != parts[0]:
                    # If the root folder changes, consider it as a new root folder
                    root_folder = parts[0]

            if len(parts) >= 2:
                subfolders.add(parts[1])

        # Root Folder Name Validation and Sub Folder Count Validation

        if root_folder.strip()!=fields["aidNumber"].strip():
            return {"code":400,"message": f"Exception Occurs due to Invalid Folder Name. Folder Name should be your Intern ID"}

        elif len(subfolders)<=2:
            return {"code":400,"message":f"Exactly 7 projects folders should be there . {len(subfolders)}  folders found" }

        else:
            return {"code":200,"message":"Validation Successful"}
    except Exception as e:
        return {"code":400,"message": f"Exception Occurs due to {repr(e)}"}

async def zipFileUploader(file,localUploadPath):
    try:
        zip_content = await file.read()

        # Create a BytesIO object to work with the ZIP content
        zip_buffer = io.BytesIO(zip_content)

        # Extract the contents of the ZIP file
        with zipfile.ZipFile(zip_buffer, "r") as zip_ref:
            # List the contents of the ZIP file
            file_list = zip_ref.namelist()

            UPLOAD_DIRECTORY = "INTERN_PORTAL_FY23/"
            # Create the upload directory if it doesn't exist
            os.makedirs(UPLOAD_DIRECTORY, exist_ok=True)

            # print(file_list)

            # os.makedirs("INTERN_PORTAL_FY23/900999/Arun_vendorportal/",exist_ok=True)

            for file_name in file_list:
                file_data = zip_ref.read(file_name)

                # Construct the file path in the upload directory
                file_path = os.path.join(UPLOAD_DIRECTORY, file_name)
                print(file_path)
                if file_path.endswith("/"):
                    os.makedirs(file_path,exist_ok=True)
                else:
                    with open(file_path,'wb') as f:
                        f.write(file_data)

        return {"code":200,"message":"Uploaded Successfully"}

    except Exception as e:
        return {"code":400,"message":f"Exception occurs due to {repr(e)}"}


import numpy as np
import scipy as sp
import cv2
import os
import glob
import pickle
import utils.MTCNN as mtcnn
import scipy

feature_file = np.load("data/feature_file.npz")
UNKNOWN_LABEL = "Unknown"
THRESHOLD = 0.75

def draw_label(image, point, label, emotion, font=cv2.FONT_HERSHEY_SIMPLEX,
                   font_scale=1, thickness=2):
        size = cv2.getTextSize(label, font, font_scale, thickness)[0]
        x, y = point
        cv2.rectangle(image, (x, y - size[1]), (x + size[0], y), (255, 0, 0), cv2.FILLED)
        cv2.putText(image, label, point, font, font_scale, (255, 255, 255), thickness)
        cv2.putText(image, emotion, (point[0], point[1]+20), font, font_scale, (255, 255, 255), thickness)

label_array = []
feature_array = []
for key in feature_file:
    label_array.append(key)
    feature_array.append(feature_file[key])

def get_label(feature):
    distance = float("inf")
    label = None

    dist = scipy.spatial.distance.cdist(feature.reshape((1, feature.size)), np.array(feature_array), 'cosine')
    closest_index = np.argmin(dist)
    distance, label = dist[0][closest_index], label_array[closest_index]

    return label if distance < THRESHOLD else UNKNOWN_LABEL

def get_margins(face_margin, margin=1):
    (x, y, w, h) = face_margin[0], face_margin[1], face_margin[2] - face_margin[0], face_margin[3] - face_margin[1]
    margin = int(min(w, h) * margin / 100)
    x_a = int(x - margin)
    y_a = int(y - margin)
    x_b = int(x + w + margin)
    y_b = int(y + h + margin)
    return (x_a, y_a, x_b - x_a, y_b - y_a)

def face_demo():
    #video_capture = cv2.VideoCapture('/home/sachin/555.mp4')
    video_capture = cv2.VideoCapture(0)
    while True:
        if not video_capture.isOpened():
            sleep(5)
        # Capture frame-by-frame
        ret, frame = video_capture.read()
        _, boundingboxes, features, emotion = mtcnn.process_image(frame)

        # placeholder for cropped faces
        for i in range(boundingboxes.shape[0]):
            (x, y, w, h) = get_margins(boundingboxes[i, 0:4])
            label = get_label(features[i]) if i < len(features) else UNKNOWN_LABEL
            #print len(features), i
            cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 200, 0), 2)
            #label = "{}".format(label)
            #draw_label(frame, (x,y), "{}{}".format(label, emotion))
            draw_label(frame, (x,y), label, emotion)

        cv2.imshow('Face Detection', frame)
        if cv2.waitKey(5) == 27 :  # ESC key press
            break
    # When everything is done, release the capture
    video_capture.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    face_demo()


import signal
import yaml
import util
from main import main_wrapper
from multiprocessing import Pool

CONFIG = """
opt:
    epochs: 10
    batch_size: 100
    alternatives: 1

    rho: 0.95
    eps: 0.000001
    lr: 1
    clip: 10

model:
    prop_embedding_size: 50
    word_embedding_size: 50
    hidden_size: 100
"""

def handle_interrupt(signal, frame):
    print("KeyboardInterrupt detected. Terminating...")
    raise KeyboardInterrupt

def train_model(hyperparams):
    hidden_size, batch_size, lr = hyperparams

    # Load the configuration from the CONFIG string
    config = util.Struct(**yaml.load(CONFIG))

    # Update the configuration
    config.opt.batch_size = batch_size
    config.opt.lr = lr
    config.model.hidden_size = hidden_size

    # Call main_wrapper() with the updated configuration
    model_name = "model_h{}_b{}_lr{}".format(hidden_size, batch_size, lr)
    main_wrapper("train.base", "abstract", config, model_name)

# Create a function to run the training using multiprocessing
def run_training(hidden_size_list, batch_size_list, lr_list):
    signal.signal(signal.SIGINT, handle_interrupt)
    p = Pool()
    try:
        for _ in p.imap(train_model, [(hidden_size, batch_size, lr) for hidden_size in hidden_size_list for batch_size in batch_size_list for lr in lr_list]):
            pass
    except KeyboardInterrupt:
        p.terminate()
        p.close()
    except Exception:
        p.terminate()
    else:
        p.close()
    p.join()

# Call the run_training() function
if __name__ == "__main__":
    hidden_size_list = [100] #[50, 100, 150]
    batch_size_list = [100] #[50, 100, 150]
    lr_list = [1.0] #[0.1, 0.5, 1.0]
    run_training(hidden_size_list, batch_size_list, lr_list)


import os
from itertools import product
import datetime
import xlwt
from django.db.models import Count
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import authenticate, login as dj_login, get_user_model, logout
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User, Group
from django.contrib.staticfiles import finders
from django.core.mail import send_mail
from django.db.models import Sum
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.template import loader
from django.template.loader import get_template
from django.urls import reverse
from django.utils.encoding import force_text
from django.utils.http import urlsafe_base64_decode
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
# from twisted.words.protocols.jabber.jstrports import client
from xhtml2pdf import pisa

from parametres.forms import PepiniereForm
from .models import Client
from parametres.models import (
    Sous_Prefecture,
    Origine,
    Prime,
    Projet,
    Activite,
    Region,
    Campagne, Cooperative, Pepiniere, Espece
)

from cooperatives.models import (
    Monitoring,
    MonitoringEspeceremplacement,
    Participantformation,
    Producteur,
    Parcelle,
    Planting,
    RemplacementMonitoring,
    Section,
    Sous_Section, Formation, Detail_Formation, DetailPlanting, Production
)

@login_required(login_url='connexion')
def client_index(request):
    # activate = "dashboard"
    cooperatives = Cooperative.objects.all()
    AllCooperatives = Cooperative.objects.all()
    nb_cooperatives = Cooperative.objects.all().count()
    nb_producteurs = Producteur.objects.all().count()
    prod_coop = Cooperative.objects.annotate(nb_producteur=Count('producteurs'))
    # section_cooperative = Cooperative.objects.annotate(nb_section=Count('sections'))
    section_coop = Cooperative.objects.annotate(nb_section=Count('sections'))
    nb_parcelles = Parcelle.objects.all().count()
    Superficie = Parcelle.objects.aggregate(total=Sum('superficie'))['total']
    Total_plant = Planting.objects.aggregate(total=Sum('plant_recus'))['total']
    production = Production.objects.aggregate(total=Sum('qteProduct'))['total']
    petite_production = Production.objects.filter(campagne="petite").aggregate(total=Sum('qteProduct'))['total']
    grande_production = Production.objects.filter(campagne="grande").aggregate(total=Sum('qteProduct'))['total']
    for coop in cooperatives:
        coop.parcelles = Parcelle.objects.filter(producteur__cooperative=coop).count()
        coop.productions = Production.objects.filter(parcelle__producteur__cooperative=coop).aggregate(total=Sum('qteProduct'))['total']
        coop.plants = Planting.objects.filter(parcelle__producteur__cooperative=coop).aggregate(total=Sum('plant_recus'))['total']
        coop.superficies = int(Parcelle.objects.filter(producteur__cooperative=coop).aggregate(total=Sum('superficie'))[ 'total'])
        # print(coop.superficies)

    context = {
        'cooperatives': cooperatives,
        'AllCooperatives': AllCooperatives,
        'nb_cooperatives': nb_cooperatives,
        'nb_producteurs': nb_producteurs,
        'nb_parcelles': nb_parcelles,
        'Superficie': Superficie,
        'Total_plant': Total_plant,
        'prod_coop': prod_coop,
        'section_coop': section_coop,
        'production': production,
        'petite_production': petite_production,
        'grande_production': grande_production,
        # 'activate': activate
        # 'section_cooperative': section_cooperative,
    }
    return render(request, 'clients/index.html', context)

@login_required(login_url='connexion')
def detail_coop(request, id=None):
    activate = "dashboard"
    cooperative = get_object_or_404(Cooperative, id=id)
    # coop_nb_producteurs =
    section = Section.objects.filter(cooperative_id=cooperative)
    sous_sections = Sous_Section.objects.filter(section__cooperative_id=cooperative)
    # section_prod = Producteur.objects.all().filter(section_id__in=section).count()
    section_prod = Section.objects.annotate(nb_producteur=Count('producteurs'))
    prod_section = Producteur.objects.filter(section_id__in=section).count()
    coop_nb_producteurs = Producteur.objects.filter(cooperative_id=cooperative).count()
    nb_formations = Formation.objects.filter(cooperative_id=cooperative).count()
    parcelles_section = Parcelle.objects.filter(producteur__section_id__in=section).count()
    # section_parcelle = Section.objects.annotate(nb_producteur=Count('producteurs.parcelles'))
    coop_nb_parcelles = Parcelle.objects.filter(producteur__section__cooperative_id=cooperative).count()
    coop_superficie = Parcelle.objects.filter(producteur__cooperative_id=cooperative).aggregate(total=Sum('superficie'))['total']
    section_superf = Parcelle.objects.filter(producteur__section_id__in=section).aggregate(total=Sum('superficie'))['total']
    coop_plants_total = DetailPlanting.objects.filter(planting__parcelle__producteur__cooperative_id=cooperative).aggregate( total=Sum('nb_plante'))['total']
    production = Production.objects.filter(parcelle__producteur__cooperative_id=cooperative).aggregate(total=Sum('qteProduct'))['total']
    petite_production = Production.objects.filter(parcelle__producteur__cooperative_id=cooperative).filter(campagne="petite").aggregate(total=Sum('qteProduct'))['total']
    grande_production = Production.objects.filter(parcelle__producteur__cooperative_id=cooperative).filter(campagne="grande").aggregate(total=Sum('qteProduct'))['total']

    context = {
        'cooperative': cooperative,
        'coop_nb_producteurs': coop_nb_producteurs,
        'coop_nb_parcelles': coop_nb_parcelles,
        'coop_superficie': coop_superficie,
        'nb_formations': nb_formations,
        'section': section,
        'sous_sections': sous_sections,
        'prod_section': prod_section,
        'section_prod': section_prod,
        # 'section_parcelle': section_parcelle,
        'parcelles_section': parcelles_section,
        'coop_plants_total': coop_plants_total,
        'section_superf': section_superf,
        'production': production,
        'petite_production': petite_production,
        'grande_production': grande_production,
        'activate': activate
        # 'labels': labels,
        # 'data': data,
    }
    return render(request, 'clients/Coop/cooperative.html', context)

@login_required(login_url='connexion')
def section_coop(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    coop_sections = Section.objects.all().filter(cooperative_id=cooperative)
    context = {
        'cooperative': cooperative,
        'coop_sections': coop_sections,
    }
    return render(request, 'clients/Coop/coop_sections.html', context)

@login_required(login_url='connexion')
def sous_section_coop(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    coop_sous_sections = Sous_Section.objects.all().filter(section__cooperative_id=cooperative)
    context = {
        'cooperative': cooperative,
        'coop_sous_sections': coop_sous_sections,
    }
    return render(request, 'clients/Coop/coop_sous_sections.html', context)

def prod_coop(request, id=None):
    activate = "producteurs"
    cooperative = get_object_or_404(Cooperative, id=id)
    coop_producteurs = Producteur.objects.all().filter(cooperative_id=cooperative)
    # coop_parcelles = Parcelle.objects.all().filter(producteur__section__cooperative_id=cooperative)
    context = {
        'cooperative': cooperative,
        'coop_producteurs': coop_producteurs,
        "activate": activate
    }
    return render(request, 'clients/Coop/coop_producteurs.html', context)

@login_required(login_url='connexion')
def parcelle_coop(request, id=None):
    activate = "parcelles"
    cooperative = get_object_or_404(Cooperative, id=id)
    # coop_producteurs = Producteur.objects.all().filter(cooperative_id=cooperative)
    coop_parcelles = Parcelle.objects.all().filter(producteur__section__cooperative_id=cooperative)
    context = {
        'cooperative': cooperative,
        'coop_parcelles': coop_parcelles,
        'activate': activate
    }
    return render(request, 'clients/Coop/coop_parcelle.html', context)

@login_required(login_url='connexion')
def planting_coop(request, id=None):
    activate = "plantings"
    cooperative = get_object_or_404(Cooperative, id=id)
    coop_plants = DetailPlanting.objects.filter(planting__parcelle__producteur__cooperative_id=cooperative)
    context = {
        'cooperative': cooperative,
        'coop_plants': coop_plants,
        "activate": activate
    }
    return render(request, 'clients/Coop/coop_plantings.html', context)

@login_required(login_url='connexion')
def projet(request):
    client = Client.objects.get(user_id=request.user.id)
    projets = Projet.objects.all().filter(client_id=client)
    context = {
        'client': client,
        'projets': projets,
    }
    return render(request, 'clients/projets.html', context)

@login_required(login_url='connexion')
def detail_proj(request, id=None):
    instance = get_object_or_404(Projet, id=id)
    # cooperatives = Cooperative.objects.filter(cooperative__projet__i).count()
    producteurs_proj = Producteur.objects.filter(cooperative__projet=instance).count()
    parcelles = Parcelle.objects.filter(producteur__cooperative__projet=instance)
    # parcelles = Planting.objects.all().filter(projet_id=instance)
    nb_parcelles_proj = Parcelle.objects.filter(producteur__cooperative__projet=instance).count()
    plants = DetailPlanting.objects.filter(planting__projet_id=instance).aggregate(total=Sum('nb_plante'))['total']
    # nb_plants_proj = Planting.objects.all().filter(projet_id = instance).count()
    superficie_proj = \
    Parcelle.objects.filter(producteur__cooperative__projet=instance).aggregate(total=Sum('superficie'))['total']

    # print(superficie_proj)
    context = {
        'instance': instance,
        'parcelles': parcelles,
        'plants': plants,
        # 'parcelles':plants,
        'nb_parcelles_proj': nb_parcelles_proj,
        # 'nb_plants_proj':nb_plants_proj,
        # 'plants':plants,
        'superficie_proj': superficie_proj,
        'producteurs_proj': producteurs_proj,
    }
    return render(request, 'clients/projet.html', context)

@login_required(login_url='connexion')
def formations(request, id=None):
    activate = "formations"
    cooperative = get_object_or_404(Cooperative, id=id)
    formations = Formation.objects.filter(cooperative_id=cooperative.id)

    for formation in formations:
        formation.nb_participant = Participantformation.objects.filter(formation_id=formation.id).count()

    context = {
        'cooperative': cooperative,
        'formations': formations,
        "activate": activate

        # 'detailFormation':detailFormation,
        # 'participants': participants,
    }
    return render(request, 'clients/Coop/coop_formations.html', context)

def detail_formation(request, id=None, _id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    formations = Formation.objects.filter(cooperative_id=cooperative.id)

    for formation in formations:
        formation.nb_participant = Participantformation.objects.filter(formation_id=formation.id).count()

    context = {
        'cooperative': cooperative,
        'formations': formations,

        # 'detailFormation':detailFormation,
        # 'participants': participants,
    }
    # print(participants)
    return render(request, 'clients/Coop/detail_formation1.html', context)

def localisation(request):
    parcelles = Parcelle.objects.all()
    context = {
        'parcelles': parcelles
    }
    return render(request, 'clients/carte.html', context)

def localisation_coop(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    # coop_producteurs = Producteur.objects.all().filter(cooperative_id=cooperative)
    points_coop = Parcelle.objects.all().filter(producteur__section__cooperative_id=cooperative)
    context = {
        'points_coop': points_coop
    }
    return render(request, 'carte1.html', context)

# EXPORT EXCEL
@login_required(login_url='connexion')
def export_prod_xls(request, id=None):
    response = HttpResponse(content_type='application/ms-excel')
    response['Content-Disposition'] = 'attachment; filename="producteurs.xls"'

    wb = xlwt.Workbook(encoding='utf-8')
    ws = wb.add_sheet('Producteurs')

    # Sheet header, first row
    row_num = 0

    font_style = xlwt.XFStyle()
    font_style.font.bold = True

    columns = ['COOPERATIVE', 'CODE', 'NOM ET PRENOMS', 'SECTION', 'LOCALITE', 'STATUT']

    for col_num in range(len(columns)):
        ws.write(row_num, col_num, columns[col_num], font_style)

    # Sheet body, remaining rows
    font_style = xlwt.XFStyle()
    cooperative = get_object_or_404(Cooperative, id=id)
    rows = Producteur.objects.all().filter(cooperative_id=cooperative.id).values_list(
        'cooperative__sigle',
        'code',
        'nom',
        'section__libelle',
        'localite',
        'type_producteur',
    )
    for row in rows:
        row_num += 1
        for col_num in range(len(row)):
            ws.write(row_num, col_num, row[col_num], font_style)

    wb.save(response)
    return response

@login_required(login_url='connexion')
def export_parcelle_xls(request, id=None):
    response = HttpResponse(content_type='application/ms-excel')
    response['Content-Disposition'] = 'attachment; filename="Parcelles.xls"'

    wb = xlwt.Workbook(encoding='utf-8')
    ws = wb.add_sheet('Parcelles')

    # Sheet header, first row
    row_num = 0

    font_style = xlwt.XFStyle()
    font_style.font.bold = True

    columns = ['COOPERATIVE', 'CODE', 'PRODUCTEUR', 'SECTION', 'LOCALITE', 'SUPER', 'LAT', 'LONG']

    for col_num in range(len(columns)):
        ws.write(row_num, col_num, columns[col_num], font_style)

    # Sheet body, remaining rows
    font_style = xlwt.XFStyle()
    cooperative = get_object_or_404(Cooperative, id=id)
    rows = Parcelle.objects.all().filter(producteur__cooperative_id=cooperative.id).values_list(
        'producteur__cooperative__sigle',
        'code',
        'producteur__nom',
        'producteur__section__libelle',
        'producteur__localite',
        'superficie',
        'latitude',
        'longitude',
    )
    for row in rows:
        row_num += 1
        for col_num in range(len(row)):
            ws.write(row_num, col_num, row[col_num], font_style)

    wb.save(response)
    return response

# EXPORT PDF
def link_callback(uri, rel):
    """
    Convert HTML URIs to absolute system paths so xhtml2pdf can access those
    resources
    """
    result = finders.find(uri)
    if result:
        if not isinstance(result, (list, tuple)):
            result = [result]
        result = list(os.path.realpath(path) for path in result)
        path = result[0]
    else:
        sUrl = settings.STATIC_URL  # Typically /static/
        sRoot = settings.STATIC_ROOT  # Typically /home/userX/project_static/
        mUrl = settings.MEDIA_URL  # Typically /media/
        mRoot = settings.MEDIA_ROOT  # Typically /home/userX/project_static/media/

        if uri.startswith(mUrl):
            path = os.path.join(mRoot, uri.replace(mUrl, ""))
        elif uri.startswith(sUrl):
            path = os.path.join(sRoot, uri.replace(sUrl, ""))
        else:
            return uri

    # make sure that file exists
    if not os.path.isfile(path):
        raise Exception(
            'media URI must start with %s or %s' % (sUrl, mUrl)
        )
    return path

@login_required(login_url='connexion')
def export_prods_to_pdf(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    producteurs = Producteur.objects.all().filter(cooperative_id=cooperative)
    template_path = 'cooperatives/producteurs_pdf.html'
    context = {
        'cooperative': cooperative,
        'producteurs': producteurs,
    }
    # Create a Django response object, and specify content_type as pdf
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="Liste Producteur.pdf"'
    # find the template and render it.
    template = get_template(template_path)
    html = template.render(context)

    # create a pdf
    pisa_status = pisa.CreatePDF(
        html, dest=response, link_callback=link_callback)
    # if error then show some funy view
    if pisa_status.err:
        return HttpResponse('Une Erreure est Survenue, Réessayer SVP...' + html + '
')
    return response

@login_required(login_url='connexion')
def export_parcelles_to_pdf(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    parcelles = Parcelle.objects.all().filter(producteur__cooperative_id=cooperative)
    template_path = 'cooperatives/new_parcelles_pdf.html'
    context = {
        'cooperative': cooperative,
        'parcelles': parcelles,
    }
    # Create a Django response object, and specify content_type as pdf
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="Liste Producteur.pdf"'
    # find the template and render it.
    template = get_template(template_path)
    html = template.render(context)

    # create a pdf
    pisa_status = pisa.CreatePDF(
        html, dest=response, link_callback=link_callback)
    # if error then show some funy view
    if pisa_status.err:
        return HttpResponse('Une Erreure est Survenue, Réessayer SVP...' + html + '
')
    return response

# Statistiques Charts
def producteur_section(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    section = Section.objects.filter(cooperative_id=cooperative)
    querysets = Producteur.objects.filter(section_id__in=section).values("section__libelle")
    labels = []
    data = []
    for stat in querysets:
        labels.append(stat['section__libelle'])
        data.append(stat['qte_recu'])

    return JsonResponse(data={
        'labels': labels,
        'data': data,
    })

def Plantings(request):
    querysets = Planting.objects.values("parcelle__producteur__cooperative__sigle").annotate(plant_recus=Sum('plant_recus'))
    labels = []
    data = []
    for stat in querysets:
        labels.append(stat['parcelle__producteur__cooperative__sigle'])
        data.append(stat['plant_recus'])

    return JsonResponse(data={
        'labels': labels,
        'data': data,
    })

def DetailPlantings(request):
    querysets = DetailPlanting.objects.values("espece__libelle").annotate(nb_plante=Sum('nb_plante'))
    labels = []
    data = []
    for stat in querysets:
        labels.append(stat['espece__libelle'])
        data.append(stat['nb_plante'])

    return JsonResponse(data={
        'labels': labels,
        'data': data,
    })

def coopdetailPlantings(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    querysets = DetailPlanting.objects.filter(planting__parcelle__producteur__cooperative_id=cooperative).values("espece__libelle").annotate(nb_plante=Sum('nb_plante'))
    labels = []
    data = []
    for stat in querysets:
        labels.append(stat['espece__libelle'])
        data.append(stat['nb_plante'])

    return JsonResponse(data={
        'labels': labels,
        'data': data,
    })

def plants_par_section(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    sections = Section.objects.filter(cooperative_id=cooperative)
    querysets = DetailPlanting.objects.filter(planting__parcelle__producteur__cooperative_id=cooperative).filter(
        planting__parcelle__producteur__section_id__in=sections).values(
        "planting__parcelle__producteur__section__libelle").annotate(nb_plante=Sum('nb_plante'))
    labels = []
    data = []
    for stat in querysets:
        labels.append(stat['planting__parcelle__producteur__section__libelle'])
        data.append(stat['nb_plante'])

    return JsonResponse(data={
        'labels': labels,
        'data': data,
    })

def prod_coop_par_campagne(request, id=None):
    cooperative = get_object_or_404(Cooperative, id=id)
    querysets = Production.objects.filter(parcelle__producteur__cooperative_id=cooperative).values("campagne").annotate(qteProduct=Sum('qteProduct'))
    labels = []
    data = []
    for stat in querysets:
        labels.append(stat['campagne'])
        data.append(stat['qteProduct'])

    return JsonResponse(data={
        'labels': labels,
        'data': data,
    })

@login_required(login_url='connexion')
def pepiniere(request):
    pepinieres = Pepiniere.objects.all()
    pepiForm = PepiniereForm()
    activate = "pepiniere"
    if request.method == 'POST':
        pepiForm = PepiniereForm(request.POST, request.FILES)
        if pepiForm.is_valid():
            pepiniere = pepiForm.save(commit=False)
            pepiniere = pepiniere.save()
            print(pepiniere)
        messages.success(request, "Site Pépinière Ajouté avec succès")
        # return HttpResponseRedirect(reverse('pepinieres'))

    context = {
        'pepinieres': pepinieres,
        'pepiForm': pepiForm,
        'activate': activate
    }
    return render(request, 'clients/pepinieres.html', context)
# Create your views here.

@login_required(login_url='connexion')
def export_formation_to_pdf(request, id=None):
    formation = get_object_or_404(Formation, id=id)
    cooperative = Cooperative.objects.get(id=formation.cooperative.id)
    nb_participant = Participantformation.objects.filter(formation_id = formation.id).count()
    participants = Participantformation.objects.filter(formation_id = id)
    producteurs = Producteur.objects.filter(cooperative_id=cooperative)
    #details = Detail_Formation.objects.filter(formation_id=instance)
    template_path = 'cooperatives/formations/pdf/formation_pdf.html'
    context = {
        'cooperative':cooperative,
        'formation':formation,
        'participants':participants,
        'nb_participant':nb_participant,
        "producteurs":producteurs
    }
    # Create a Django response object, and specify content_type as pdf
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="Formation.pdf"'
    # find the template and render it.
    template = get_template(template_path)
    html = template.render(context)

    # create a pdf
    pisa_status = pisa.CreatePDF(
        html, dest=response, link_callback=link_callback)
    # if error then show some funy viewp
    if pisa_status.err:
        return HttpResponse('Une Erreure est Survenue, Réessayer SVP...' + html + '
')
    return response

def producteur(request):
    producteurs = Producteur.objects.all()
    for prod in producteurs:
        prod.superficies = Parcelle.objects.filter(producteur=prod).aggregate(total=Sum('superficie'))['total']
    ctx = {
        'producteurs' : producteurs
    }
    return render(request, 'clients/producteurs.html', ctx)

def parcelle(request):
    parcelles = Parcelle.objects.all()
    for p in parcelles:
        p.plants_recus = Planting.objects.filter(parcelle=p).aggregate(total=Sum('plant_recus'))['total']
        p.plants_plante = DetailPlanting.objects.filter(planting__parcelle=p).aggregate(total=Sum('nb_plante'))['total']
    ctx = {
        'parcelles' : parcelles
    }
    return render(request, 'clients/parcelles.html', ctx)

def production(request):
    productions = Production.objects.all()
    ctx = {
        'productions': productions
    }
    return render(request, 'clients/production.html', ctx)

def planting(request):
    plantings = Planting.objects.all().order_by('-date')

    ctx = {
        'plantings': plantings
    }
    return render(request, 'clients/plantings.html', ctx)

def detail_planting(request, id=None):
    activate = "plantings"
    instance = get_object_or_404(Planting, id=id)
    Details_Planting = DetailPlanting.objects.filter(planting_id=instance)

    Monitorings = Monitoring.objects.filter(planting_id=instance)
    remplacements = RemplacementMonitoring.objects.filter(monitoring__planting_id = instance.id)

    for remp in remplacements :
        remp.mort = MonitoringEspeceremplacement.objects.filter(remplacement_id = remp.id).aggregate(total=Sum('mort'))['total']
        remp.remplacer = MonitoringEspeceremplacement.objects.filter(remplacement_id = remp.id).aggregate(total=Sum('remplacer'))['total']
        #print(remp.mort)

    for monitoring in Monitorings:
        try:
            monitoring.remplacer = RemplacementMonitoring.objects.filter(monitoring_id = monitoring.id).latest('id')

        except RemplacementMonitoring.DoesNotExist:
            pass

    try:
        lastMonitoring = Monitoring.objects.filter(planting_id=instance).latest('id')
        context = {
            'instance':instance,
            'Details_Planting':Details_Planting,
            'Monitorings':Monitorings,
            "activate": activate,
            "lastMonitoring":lastMonitoring,
            "remplacements":remplacements

         }

    except Monitoring.DoesNotExist:
             context = {
            'instance':instance,
            'Details_Planting':Details_Planting,
            'Monitorings':Monitorings,
            "activate": activate,
            "remplacements":remplacements

         }

    return render(request, 'clients/Coop/detail_planting.html', context)

def export_planting_xls(request):
    response = HttpResponse(content_type='application/ms-excel')
    response['Content-Disposition'] = 'attachment; filename="producteurs.xls"'

    wb = xlwt.Workbook(encoding='utf-8')
    ws = wb.add_sheet('Producteurs')

    # Sheet header, first row
    row_num = 0

    font_style = xlwt.XFStyle()
    font_style.font.bold = True

    columns = ['CODE', 'PRODUCTEUR', 'SECTION', 'ESPECE', 'QTE']

    for col_num in range(len(columns)):
        ws.write(row_num, col_num, columns[col_num], font_style)

    # Sheet body, remaining rows
    font_style = xlwt.XFStyle()
    rows = DetailPlanting.objects.all().values_list(
        'planting__parcelle__code',
        'planting__parcelle__producteur__nom',
        'planting__parcelle__producteur__section__libelle',
        'espece__libelle',
        'nb_plante',
    )
    for row in rows:
        row_num += 1
        for col_num in range(len(row)):
            ws.write(row_num, col_num, row[col_num], font_style)

    wb.save(response)
    return response


from mushroom_hierarchical.visualization_tools.visualize_ship_steering import visualize_ship_steering
import matplotlib.pyplot as plt
from mushroom_hierarchical.visualization_tools.visualize_control_block import visualize_control_block
from mushroom_hierarchical.visualization_tools.visualize_policy_parameters import visualize_policy_params
from mushroom_hierarchical.utils.pick_last_ep_dataset import pick_last_ep
from mushroom_hierarchical.utils.pick_eps import pick_eps
from mushroom.utils.dataset import compute_J
import numpy as np
from tqdm import tqdm

def visualize_big_hierarchical(folder, gamma=1, epochs=None, range_vis=None):

    fig = plt.figure()
    ax1 = fig.add_subplot(121)
    ax2 = fig.add_subplot(122)

    experiment_params = np.load(folder+'/experiment_params_dictionary.npy')
    how_many = experiment_params.item().get('how_many')
    n_runs = experiment_params.item().get('n_runs') + 1
    ep_per_run = experiment_params.item().get('eval_run')
    parameter_dataset1 = list()
    parameter_dataset2 = list()
    size_eps = list()
    size_exp_list = list()
    J_avg = np.zeros((n_runs,))
    size_eps_avg = np.zeros((n_runs,))
    J_exp = []
    ep_step_no = 0

    for exp_no in range(how_many):

        parameter_dataset1 += [np.load(folder+'/' + str(exp_no) + '/parameter_dataset1_file.npy')]
        parameter_dataset2 += [np.load(folder+'/' + str(exp_no) + '/parameter_dataset2_file.npy')]
        dataset_eval = np.load(folder+'/' + str(exp_no) + '/dataset_eval_file.npy')

        J_runs_eps = compute_J(dataset_eval, gamma)
        for i in range(n_runs):
            J_avg[i] = np.mean(J_runs_eps[ep_per_run * i:ep_per_run * i + ep_per_run], axis=0)

        for dataset_step in dataset_eval:
            if not dataset_step[-1]:
                ep_step_no += 1
            else:
                size_eps.append(ep_step_no)
                ep_step_no = 0
        J_exp.append(J_avg)
        J_avg = np.zeros((n_runs,))

        for i in range(n_runs):
            size_eps_avg[i] = np.mean(size_eps[ep_per_run * i:ep_per_run * i + ep_per_run], axis=0)

        size_exp_list.append(size_eps_avg)
        size_eps = list()
        size_eps_avg = np.zeros((n_runs,))

    J_all_avg = np.mean(J_exp, axis=0)
    J_all_avg_err = np.std(J_exp, axis=0) / np.sqrt(how_many) * 1.96 if how_many > 1 else 0
    size_all_avg = np.mean(size_exp_list, axis=0)
    size_all_avg_err = np.std(size_all_avg, axis=0) / np.sqrt(how_many) * 1.96 if how_many > 1 else 0
    ax1.errorbar(x=np.arange(n_runs) + 1, y=J_all_avg, yerr=J_all_avg_err)
    ax1.set_title('J_eps_averaged')

    ax2.errorbar(x=np.arange(n_runs) + 1, y=size_all_avg, yerr=size_all_avg_err)
    ax2.set_title('size_eps_averaged')

    dataset_eval = np.load(folder+'/' + str(how_many - 1) + '/dataset_eval_file.npy')
    low_level_dataset = np.load(folder+'/' + str(exp_no) + '/low_level_dataset_file.npy')

    small = False
    for run in epochs:
        dataset_eval_vis = list()
        dataset_eval_run = pick_eps(dataset_eval, start=run * ep_per_run,
                                    end=run * ep_per_run + ep_per_run)
        eps_of_run = dataset_eval_run[:5]

        for ep in eps_of_run:
            for step in ep:
                dataset_eval_vis.append(step)
        visualize_ship_steering(dataset_eval_vis, 'epoch ' + str(run),
                                small=small, range_eps=range_vis)

    visualize_policy_params(parameter_dataset1, parameter_dataset2, small=small, how_many=how_many)
    visualize_control_block(datalist_control=low_level_dataset, ep_count=5, how_many=how_many)

    plt.show()

if __name__ == '__main__':
    #folder = '/home/dave/Documenti/results_idil/2018-03-30_02-07-43_big_hierarchical'
    folder = 'latest'

    epochs=[0, 6, 12, 24]
    # range_vis must be a range()
    visualize_big_hierarchical(folder, gamma=0.99, epochs=epochs, range_vis=None)


import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread("1.jpg", 0)
"""hist = cv2.calcHist([img],[0],None,[256],[0,256])

plt.subplot(121),plt.imshow(img,"gray")
plt.subplot(122),plt.plot(hist)
plt.xlim([0,256])
plt.show()"""

ret, binario = cv2.threshold(img, 80, 255, cv2.THRESH_BINARY)
cv2.imshow("Imagen - Binarios", binario)
# print("retval: " + str(ret))

kernel = np.ones((6, 6), np.uint8)
erosion = cv2.erode(binario, kernel)
cv2.imshow("erosion", erosion)

dilatacion = cv2.dilate(erosion, kernel, iterations=2)
cv2.imshow("dilatacion", dilatacion)

_, contorno, _ = cv2.findContours(image=erosion, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(img, contorno, -1, (0, 255, 0), 2)
cv2.imshow("Imagen - Contorno", img)

count_5cents = {"cantidad": 0, "total": 0}
count_10cents = {"cantidad": 0, "total": 0}

for cont in contorno:
    cont = cv2.contourArea(cont)
    if cont > 1800:
        count_5cents["cantidad"] += 1
        count_5cents["total"] += 5
    elif cont < 1700:
        count_10cents["cantidad"] += 1
        count_10cents["total"] += 10

print("Total de Dinero: ", count_10cents["total"] + count_5cents["total"], "centavos")
print("Total de Monedas: ", count_10cents["cantidad"] + count_5cents["cantidad"], "monedas")
print("Total de Monedas de 5 centavos: ", count_5cents["cantidad"])
print("Total de Monedas de 10 centavos: ", count_10cents["cantidad"])

"""
cv2.imshow('ventana', img)

print img.shape
print img.size
print img.dtype
"""

cv2.waitKey(0)


from collections import deque

N = int(input())        # queue = [1, 2, 3, 4, 5, 6, 7]
nums = deque([i+1 for i in range(N)]) # 1~N까지를 큐로 만듦

while len(nums) != 1:               # 한 개 남을 때 까지 반복
    print(nums.popleft(), end=' ')  # 제일 위(맨 왼쪽)버리면서 출력
    tmp = nums.popleft()            # 제일 밑(맨 오른쪽)으로 넣음
    nums.append(tmp)

print(nums[0])


import attr
import sys

@attr.s(frozen=True, slots=True)
class Node:
  children = attr.ib()
  metadata = attr.ib()

stream = list(map(int,sys.stdin.readline().split()))

def read(stream, i):
  nchildren = stream[i]
  i += 1
  nmeta = stream[i]
  i += 1

  children = []
  for j in range(nchildren):
    ch, i = read(stream, i)
    children.append(ch)
  meta = stream[i:i+nmeta]
  i += nmeta
  return (Node(children, meta), i)

root, i = read(stream, 0)
assert i == len(stream)

def csum(node):
  return sum(node.metadata) + sum(map(csum, node.children))

print("metasum={}".format(csum(root)))

def value(node):
  if not node.children:
    return sum(node.metadata)
  cvs = list(map(value, node.children))
  return sum(cvs[i-1] for i in node.metadata if i > 0 and i <= len(cvs))

print("value={}".format(value(root)))


import datetime
import numpy as np
import pandas as pd
import mysql.connector
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, LSTM

# Mengambil data dari database MySQL
mydb = mysql.connector.connect(
  host="localhost",
  user="root",
  password="",
  database="pengujian"
)

# Mengambil data dari database MySQL
mycursor = mydb.cursor()
mycursor.execute("SELECT * FROM honda")
data = mycursor.fetchall()

# Membuat DataFrame dari data MySQL
df = pd.DataFrame(data, columns=['tahun', 'minat', 'trand', 'penjualan'])

# Mengubah tipe data kolom 'sales' menjadi float
df['penjualan'] = df['penjualan'].astype(float)

# Menormalkan data menggunakan MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(df[['penjualan']])

# Membuat fungsi untuk membuat dataset dengan time steps
def create_dataset(data, time_steps=1):
    X_data, Y_data = [], []
    for i in range(len(data)-time_steps):
        X_data.append(data[i:(i+time_steps), 0])
        Y_data.append(data[i+time_steps, 0])
    return np.array(X_data), np.array(Y_data)

# Membuat dataset untuk training dan testing
time_steps = 12 # Contoh: menggunakan 12 bulan sebelumnya untuk memprediksi penjualan bulan berikutnya
data, _ = create_dataset(scaled_data, time_steps)

# Reshape data agar sesuai dengan format input LSTM
data = np.reshape(data, (data.shape[0], data.shape[1], 1))

# Membuat model LSTM
model = Sequential()
model.add(LSTM(50, return_sequences=True, input_shape=(time_steps, 1)))
model.add(LSTM(50))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(data, scaled_data[time_steps:], epochs=100, batch_size=16, verbose=2)

# Menggunakan model untuk memprediksi penjualan kendaraan bermotor selama 3 tahun ke depan
last_data = scaled_data[-time_steps:]
future_predictions = []
for i in range(36): # Memprediksi selama 3 tahun ke depan
    input_data = np.reshape(last_data, (1, time_steps, 1))
    prediction = model.predict(input_data)
    future_predictions.append(prediction[0][0])
    last_data = np.append(last_data[1:], prediction[0][0])

# Invers scaling untuk mendapatkan hasil yang sebenarnya
future_predictions = scaler.inverse_transform(np.array(future_predictions).reshape(-1, 1))

# Menambahkan nilai tahun ke data prediksi
last_year = df['tahun'].max()
future_years = [str(last_year+datetime.timedelta(i+1)) for i in range(len(future_predictions))]
future_predictions_df = pd.DataFrame(columns=['tahun', 'penjualan'])
future_predictions_df['tahun'] = future_years
future_predictions_df['penjualan'] = float('nan')

for i, prediction in enumerate(future_predictions):
    future_predictions_df.loc[future_predictions_df['tahun']==future_years[i], 'penjualan'] = prediction

# Gabungkan DataFrame df dengan future_predictions_df
df = pd.concat([df, future_predictions_df])

# Cetak DataFrame hasil gabungan
print(df)