import gym
import torch
import numpy as np
from typing import Any, Callable, Dict, List, Union, Tuple

from syllabus.curricula.plr import TaskSampler
from syllabus.core import Curriculum, UsageError, enumerate_axes
from syllabus.task_space import TaskSpace

class RolloutStorage(object):
    def __init__(self, num_steps: int, num_processes: int, requires_value_buffers: bool, action_space: gym.Space = None):
        self._requires_value_buffers = requires_value_buffers
        self.tasks = torch.zeros(num_steps, num_processes, 1, dtype=torch.int)
        self.masks = torch.ones(num_steps + 1, num_processes, 1)

        if requires_value_buffers:
            self.returns = torch.zeros(num_steps + 1, num_processes, 1)
            self.rewards = torch.zeros(num_steps, num_processes, 1)
            self.value_preds = torch.zeros(num_steps + 1, num_processes, 1)
        else:
            if action_space is None:
                raise ValueError("Action space must be provided to PLR for strategies 'policy_entropy', 'least_confidence', 'min_margin'")
            self.action_log_dist = torch.zeros(num_steps, num_processes, action_space.n)

        self.num_steps = num_steps
        self.step = 0

    def to(self, device):
        self.masks = self.masks.to(device)
        self.tasks = self.tasks.to(device)
        if self._requires_value_buffers:
            self.rewards = self.rewards.to(device)
            self.value_preds = self.value_preds.to(device)
            self.returns = self.returns.to(device)
        else:
            self.action_log_dist = self.action_log_dist.to(device)

    def insert(self, masks, action_log_dist=None, value_preds=None, rewards=None, tasks=None):
        if self._requires_value_buffers:
            assert value_preds is not None and rewards is not None, f"Selected strategy {self._requires_value_buffers} requires value_preds and rewards"
            if len(rewards.shape) == 3:
                rewards = rewards.squeeze(2)
            self.value_preds[self.step].copy_(torch.as_tensor(value_preds))
            self.rewards[self.step].copy_(torch.as_tensor(rewards)[:, None])
            self.masks[self.step + 1].copy_(torch.as_tensor(masks)[:, None])
        else:
            self.action_log_dist[self.step].copy_(action_log_dist)
        if tasks is not None:
            assert isinstance(tasks[0], int), "Provided task must be an integer"
            self.tasks[self.step].copy_(torch.as_tensor(tasks)[:, None])
        self.step = (self.step + 1) % self.num_steps

    def after_update(self):
        self.masks[0].copy_(self.masks[-1])

    def compute_returns(self,
                        next_value,
                        gamma,
                        gae_lambda):
        self.value_preds[-1] = next_value
        gae = 0
        for step in reversed(range(self.rewards.size(0))):
            delta = self.rewards[step] + gamma * self.value_preds[step + 1] * self.masks[step + 1] - self.value_preds[step]
            gae = delta + gamma * gae_lambda * self.masks[step + 1] * gae
            self.returns[step] = gae + self.value_preds[step]

class PrioritizedLevelReplay(Curriculum):
    REQUIRES_STEP_UPDATES = False
    REQUIRES_CENTRAL_UPDATES = True
    def __init__(self,
                 task_space: TaskSpace,
                 *curriculum_args,
                 task_sampler_kwargs_dict: dict = {},
                 action_space: gym.Space = None,
                 device: str = "cuda",
                 num_steps: int = 256,
                 num_processes: int = 64,
                 gamma: float = 0.999,
                 gae_lambda: float = 0.95,
                 suppress_usage_warnings=False,
                 **curriculum_kwargs):
        self._strategy = task_sampler_kwargs_dict.get("strategy", None)
        if not isinstance(task_space.gym_space, gym.spaces.Discrete) and not isinstance(task_space.gym_space, gym.spaces.MultiDiscrete):
            raise ValueError(f"Task space must be discrete or multi-discrete, got {task_space.gym_space}.")
        if "num_actors" in task_sampler_kwargs_dict:
            print(f"Overwriting 'num_actors' {task_sampler_kwargs_dict['num_actors']} in task sampler kwargs with PLR num_processes {num_processes}.")

        task_sampler_kwargs_dict["num_actors"] = num_processes
        super().__init__(task_space, *curriculum_args, **curriculum_kwargs)
        self._num_steps = num_steps             # Number of steps stored in rollouts and used to update task sampler
        self._num_processes = num_processes     # Number of parallel environments
        self._gamma = gamma
        self._gae_lambda = gae_lambda
        self._supress_usage_warnings = suppress_usage_warnings
        #self.tasks = self._enumerate_tasks(task_space)
        self._task2index = {task: i for i, task in enumerate(self.tasks)}
        self._task_sampler = TaskSampler(self.tasks, action_space=action_space, **task_sampler_kwargs_dict)
        self._rollouts = RolloutStorage(self._num_steps, self._num_processes, self._task_sampler.requires_value_buffers, action_space=action_space)
        self._rollouts.to(device)
        self.num_updates = 0    # Used to ensure proper usage
        self.num_samples = 0    # Used to ensure proper usage

    def update_on_demand(self, metrics: Dict):
        """
        Update the curriculum with arbitrary inputs.
        """
        self.num_updates += 1
        try:
            masks = metrics["masks"]
            tasks = metrics["tasks"]
            tasks = [self._task2index[t] for t in tasks]
        except KeyError as e:
            raise KeyError("Missing or malformed PLR update. Must include 'masks', and 'tasks'") from e

        # Parse optional update values (required for some strategies)
        value = next_value = rew = action_log_dist = None
        if self._task_sampler.requires_value_buffers:
            if "value" not in metrics or "rew" not in metrics:
                raise KeyError(f"'value' and 'rew' must be provided in every update for the strategy {self._strategy}.")
            value = metrics["value"]
            rew = metrics["rew"]
        else:
            if "action_log_dist" not in metrics:
                raise KeyError(f"'action_log_dist' must be provided in every update for the strategy {self._strategy}.")
            action_log_dist = metrics["action_log_dist"]

        # Update rollouts
        self._rollouts.insert(masks, action_log_dist=action_log_dist, value_preds=value, rewards=rew, tasks=tasks)

        # Update task sampler
        if self._rollouts.step == 0:
            if self._task_sampler.requires_value_buffers:
                if "next_value" not in metrics:
                    raise KeyError("'next_value' must be provided in the update every {self.num_steps} steps for the strategy {self._strategy}.")
                next_value = metrics["next_value"]
                self._rollouts.compute_returns(next_value, self._gamma, self._gae_lambda)
            self._task_sampler.update_with_rollouts(self._rollouts)
            self._rollouts.after_update()
            self._task_sampler.after_update()

    def _sample_distribution(self) -> List[float]:
        """
        Returns a sample distribution over the task space.
        """
        return self._task_sampler.sample_weights()

    def sample(self, k: int = 1) -> Union[List, Any]:
        self.num_samples += 1
        return [self._task_sampler.sample() for k in range(k+1)]

    def update_on_step(self, obs, rew, done, info) -> None:
        """
        Update the curriculum with the current step results from the environment.
        """
        raise NotImplementedError("PrioritizedLevelReplay does not support the step updates. Use on_demand from the learner process.")

    def update_on_step_batch(self, step_results: List[Tuple[int, int, int, int]]) -> None:
        """
        Update the curriculum with a batch of step results from the environment.
        """
        raise NotImplementedError("PrioritizedLevelReplay does not support the step updates. Use on_demand from the learner process.")

    def update_on_episode(self, episode_return: float, trajectory: List = None) -> None:
        """
        Update the curriculum with episode results from the environment.
        """
        raise NotImplementedError("PrioritizedLevelReplay does not support the episode updates. Use on_demand from the learner process.")

    def update_task_progress(self, task: Any, success_prob: float) -> None:
        """
        Update the curriculum with a task and its success probability upon
        success or failure.
        """
        if not self._supress_usage_warnings and self.num_updates == 0 and self.num_samples > self._num_processes * 2:
            raise UsageError("PLR has not been updated yet. Please call update_curriculum() in your learner process.")

    def _enumerate_tasks(self, space):
        assert isinstance(space, gym.spaces.Discrete) or isinstance(space, gym.spaces.MultiDiscrete), f"Unsupported task space {space}: Expected Discrete or MultiDiscrete"
        if isinstance(space, gym.spaces.Discrete):
            return list(range(space.n))
        else:
            return list(enumerate_axes(space.nvec))


"""
The sensor module contains classes to define point or camera sensors
that can either be stationary and mobile.

.. rubric:: Contents

.. autosummary::

    Sensor
    Position
    Stationary
    Mobile
    Detector
    Point
    Camera
"""
from __future__ import print_function, division
import pandas as pd
import numpy as np
from scipy.interpolate import griddata
from scipy.spatial.distance import cdist
from scipy import integrate
from scipy import ndimage as sn

class Sensor(object):

    def __init__(self, position=None, detector=None):
        """
        Defines a sensor object and methods to calculate detection.

        Parameters
        ----------
        position : chama Position
            Sensor position
        detector : chama Detector
            Sensor detector, determines the method used to calculate detection
        """

        self.name = None

        if position is None or not isinstance(position, Position):
            raise ValueError('Must specify a Position object to create a '
                             'Sensor')
        self.position = position

        if detector is None or not isinstance(detector, Detector):
            raise ValueError('Must specify a Detector object to create a '
                             'Sensor')
        self.detector = detector

    def get_detected_signal(self, signal, interp_method=None,
                            min_distance=10.0):
        """
        Returns the detected signal.
        """
        return self.detector.get_detected_signal(signal, self.position,
                                                 interp_method, min_distance)

class Position(object):

    def __init__(self, location=None):
        """
        Defines a sensor's position.

        Parameters
        ----------
        location : (x,y,z) tuple or index
            The location of the Position object defined as an (x,y,z) tuple
            or location index, which can be a string or integer
        """
        self.location = location

    def __call__(self, time):
        """
        Returns the location at the specified time

        Parameters
        ----------
        time : int or float

        Returns
        -------
        Location tuple (x,y,z) or (j)

        """
        if isinstance(self.location, tuple):
            return tuple(self.location)
        else:
            return tuple([self.location])

class Stationary(Position):
    """
    Defines a stationary sensor's position.
    """
    pass

class Mobile(Position):
    """
    Defines a mobile sensor's position.
    A mobile position moves according to defined waypoints and speed. The
    mobile position is assumed to move in a straight line between waypoints
    and will repeat its path if needed.

    Parameters
    ----------
    locations : list of (x,y,z) tuples
        List of (x,y,z) tuples defining the waypoints of the mobile
        sensor's path.
    speed : int or float
        The speed of the mobile sensor in units consistent
        with the waypoints and sensor sample_times
    repeat : bool
        Boolean indicating if the path should repeat
    """
    def __init__(self, locations=None, speed=1, start_time=0, repeat=False):
        super(Mobile, self).__init__(locations)
        self.speed = speed
        self.start_time = start_time
        self.repeat = repeat
        self._d_btwn_locs = None

    def __call__(self, time):
        """
        Returns the position (x,y,z) at the specified time.

        Parameters
        ----------
        time : int or float

        Returns
        -------
        tuple
            The (x,y,z) location

        """
        # Calculate distance traveled at specified time
        delta_time = time - self.start_time
        if delta_time < 0:
            delta_time = 0
        distance = self.speed * delta_time

        temp_locs = [np.array(i) for i in self.location]
        if self.repeat:  # if path repeats
            temp_locs.append(temp_locs[0])

        if self._d_btwn_locs is None:
            # Distances between consecutive points
            self._d_btwn_locs = \
                [np.linalg.norm(temp_locs[i] - temp_locs[i + 1])
                 for i in range(len(temp_locs) - 1)]

        if self.repeat:  # if path repeats
            while distance > sum(self._d_btwn_locs):
                distance -= sum(self._d_btwn_locs)
        else:
            if distance > sum(self._d_btwn_locs):
                distance = sum(self._d_btwn_locs)

        i = 0
        # Figure out which line segment
        for i, _ in enumerate(self._d_btwn_locs):
            if sum(self._d_btwn_locs[:i + 1]) >= distance:
                distance -= sum(self._d_btwn_locs[:i])
                break

        # The two waypoints defining the line segment
        loc1 = temp_locs[i]
        loc2 = temp_locs[i + 1]

        location = loc1 + (loc2 - loc1) * (distance / self._d_btwn_locs[i])

        return tuple(location)

class Detector(object):
    """
    Defines a sensor's detector.

    Parameters
    ----------
    threshold : int
        The minimum signal that can be detected by the sensor
    sample_times : list of ints or floats
        List of the sensor's sample/measurement times
    """

    def __init__(self, threshold=None, sample_times=None):
        self.threshold = threshold
        self.sample_times = sample_times
        self.sample_points = None

    def get_sample_points(self, position):
        """
        Returns the sensor sample points in the form (t,x,y,z) or (t,j)

        Parameters
        ----------
        position : chama Position
            The position of the sensor

        Returns
        -------
        A list of sample points in the form (t,x,y,z) or (t,j)
        """

        if self.sample_points is None:
            self.sample_points = [(t,) + position(t) for t in
                                  self.sample_times]
        return self.sample_points

    def get_detected_signal(self, signal, position, interp_method,
                            min_distance):
        """
        Returns the signal detected by the sensor.

        Parameters
        ----------
        signal : pandas DataFrame
            DataFrame with the multi-index (T, X, Y, Z) or (T, Node) and columns
            containing the concentrations for different scenarios
        position : chama Position
            The position of the sensor
        interp_method : 'linear', 'nearest', or None
            Method used to interpolate the signal if needed.
            A value of 'linear' will use griddata to interpolate missing
            sample points. A value of 'nearest' will set the sample point to
            the nearest signal point within a minimum distance of min_distance.
            If there are no signal points within this distance then the
            signal will be set to zero at the sample point.
        min_distance : float
            The minimum distance when using the 'nearest' interp_method

        Returns
        -------
        A pandas Series with multi-index (T, Scenario) and signal values above
        the sensor threshold.

        """
        pts = self.get_sample_points(position)
        if len(pts) == 0:
            return pd.Series()

        signal_sample = self._get_signal_at_sample_points(signal, pts,
                                                          interp_method,
                                                          min_distance)
        if len(signal_sample) == 0:
            return pd.Series()

        # Reset the index
        signal_sample = signal_sample.reset_index()

        # At this point we don't need the X,Y,Z or Node columns
        if set(['X', 'Y', 'Z']) < set(list(signal_sample.columns)):
            signal_sample.drop(['X', 'Y', 'Z'], inplace=True, axis=1)
        elif set(['Node']) < set(list(signal_sample.columns)):
            signal_sample.drop(['Node'], inplace=True, axis=1)
        else:
            raise ValueError('Unrecognized signal format')
            return

        # Set T as the index
        signal_sample = signal_sample.set_index('T')

        # Apply threshold
        signal_sample = signal_sample[signal_sample >= self.threshold]
        if len(signal_sample) == 0:
            return pd.Series()

        # Name the columns so that the index is labeled after stacking
        signal_sample.columns.name = 'Scenario'

        # Drop Nan and stack by index
        return signal_sample.stack()

    def _get_signal_at_sample_points(self, signal, sample_points,
                                     interp_method, min_distance):
        raise NotImplementedError()

class Point(Detector):
    """
    Defines a point sensor.
    """

    def _get_signal_at_sample_points(self, signal, sample_points,
                                     interp_method, min_distance):
        """
        Returns the signal at the sensor sample points. If a sample point
        does not exist in the signal DataFrame then interpolate the signal.

        Parameters
        -----------
        signal : pandas DataFrame

        sample_points : list of tuples

        interp_method : 'linear', 'nearest', or None

        min_distance : float

        Returns
        ---------
        pandas DataFrame has a multi-index containing all of the
        sample_points and columns for each scenario with the
        concentration at each sample point

        """

        # Get subset of signal. If a sample point is not in signal then NaN
        # is inserted
        try:
            signal_subset = signal.loc[sample_points, :]
        except: # Some sample points are not in the signal
            if ['T', 'X', 'Y', 'Z'] == signal.index.names:
                signal_subset = pd.DataFrame(data=np.nan, columns=signal.columns,
                                         index=pd.MultiIndex.from_tuples(sample_points, names=['T', 'X', 'Y', 'Z']))
            elif ['T', 'Node'] == signal.index.names:
                signal_subset = pd.DataFrame(data=np.nan, columns=signal.columns,
                                         index=pd.MultiIndex.from_tuples(sample_points, names=['T', 'Node']))
            else:
                raise ValueError('Unrecognized signal format')

            sample_points_in_signal = list(set(sample_points).intersection(set(signal.index)))
            signal_subset.loc[sample_points_in_signal, :] = signal.loc[sample_points_in_signal, :]

        if interp_method is None:
            return signal_subset

        # Get the sample_points that need to be interpolated
        temp = signal_subset.isnull().any(axis=1)  # Get rows containing NaN
        interp_points = list(signal_subset[temp].index)  # Get their index

        if len(interp_points) == 0:
            return signal_subset

        # TODO: Revisit the distance calculation.
        # Scaling issue by including both time and xyz location in distance
        # calculation. Manually select the signal times bordering
        # interp_point times BEFORE calculating the distance? Or include a
        # time scaling parameter as an additional input?

        # get the distance between the signal points and the interp_points
        signal_points = list(signal.index)
        distdata = cdist(signal_points, interp_points)

        # Might not want to build this data frame when signal is very large
        dist = pd.DataFrame(data=distdata, index=signal.index)

        if interp_method == 'linear':

            # Loop over interp_points
            for i in range(len(dist.columns)):
                temp = dist.iloc[:, i]

                # Get the rows within dist_factor of the minimum distance
                dist_factor = 2
                temp2 = temp[temp < temp.min() * dist_factor]
                # Ensures that we get enough points to do the interpolation
                while len(temp2) < 100:
                    dist_factor += 1
                    temp2 = temp[temp < temp.min() * dist_factor]
                temp_signal = signal.loc[temp2.index, :]

                # Loop over scenarios
                for j in signal.columns:

                    interp_signal = griddata(list(temp_signal.index),
                                             list(temp_signal.loc[:, j]),
                                             interp_points[i],
                                             method=interp_method,
                                             rescale=True)
                    if np.isnan(interp_signal):
                        raise ValueError('Trying to interpolate a sample '
                                         'point outside of the signal grid. '
                                         'Make sure that all sensor '
                                         'locations are contained in the '
                                         'area spanned by the signal data.')
                    signal_subset.at[interp_points[i], j] = interp_signal

        elif interp_method == 'nearest':

            # Loop over interp_points
            for i in range(len(dist.columns)):
                temp = dist.iloc[:, i]

                if temp.min() > min_distance:
                    # Loop over scenarios
                    for j in signal.columns:
                        interp_signal = 0.0
                        signal_subset.at[interp_points[i], j] = interp_signal
                else:
                    temp2 = temp[temp < min_distance]
                    temp_signal = signal.loc[temp2.index, :]

                    # Loop over scenarios
                    for j in signal.columns:

                        interp_signal = griddata(list(temp_signal.index),
                                                 list(temp_signal.loc[:, j]),
                                                 interp_points[i],
                                                 method=interp_method,
                                                 rescale=True)

                        signal_subset.at[interp_points[i], j] = interp_signal
        else:
            raise ValueError('Unrecognized or unsupported interpolation method'
                             ' "%s" was specified. Only "linear" or "nearest" '
                             'interpolations are supported' % interp_method)

        return signal_subset

class Camera(Detector):
    """
    Defines a camera sensor.

    Parameters
    ----------
    threshold : int
        The minimum number of pixels that must detect something in order for
        the camera to detect.
    sample_times : list of ints or floats
        List of the sensor's sample/measurement times
    direction : (x, y, z) tuple
        Tuple representing the direction that the camera is pointing in (x,
        y, z) coordinates relative to the origin
    **kwds : dictionary
        Keyword arguments for setting parameter values in the camera model
    """

    def __init__(self, threshold=None, sample_times=None,
                 direction=(1, 1, 1), **kwds):

        super(Camera, self).__init__(threshold, sample_times)

        # Direction of the camera relative to the origin
        self.direction = direction

        # Set default camera properties

        # Transmission coefficient of air
        self.tau_air = kwds.pop('tau_air', 1)

        # Maximum distance that the camera can detect in (m)
        self.dist = kwds.pop('dist', 500.0)

        # TODO: Get descriptions of these from Arvind
        self.netd = kwds.pop('netd', 0.015)
        self.f_number = kwds.pop('f_number', 1.5)
        self.e_a = kwds.pop('e_a', 0.1)
        self.e_g = kwds.pop('e_g', 0.5)
        self.T_g = kwds.pop('T_g', 300)
        self.T_plume = kwds.pop('T_plume', 300)
        self.lambda1 = kwds.pop('lambda1', 3.2E-6)
        self.lambda2 = kwds.pop('lambda2', 3.4E-6)
        self.fov1 = kwds.pop('fov1', 24 * np.pi / 180)
        self.fov2 = kwds.pop('fov2', 18 * np.pi / 180)
        self.a_d = kwds.pop('a_d', 9.0E-10)
        self.Kav = kwds.pop('Kav', 2.191e-20)

        # Constants used in the camera model
        self.NA = 6.02E23  # Avogadro's number
        self.h = 6.626e-34  # Planck's constant [J-s]
        self.SIGMA = 5.67e-8  # Stefan-Boltzmann constant [W/m^2-K^4]
        self.c = 3.0e8  # Speed of light [m/s]
        self.k = 1.38e-23  # Boltzmann's constant [J/K]

    def _get_signal_at_sample_points(self, signal, sample_points,
                                     interp_method, min_distance):
        """
        Defines detection as seen by a camera sensor. Not just
        selecting/interpolating a subset of the signal DataFrame. We are using
        the CONCENTRATION signal DataFrame to calculate the PIXEL signal at the
        sample points.

        Parameters
        -----------
        signal : pandas DataFrame
            DataFrame has a multi-index with (T, X, Y, Z) points
            and each column in the frame contains concentration
            values at those points for one scenario

        sample_points : list of tuples (t,x,y,z)

        Returns
        ---------
        pandas DataFrame has a multi-index with the sensor's sample_points
        (T,X,Y,Z) and each column contains the number of pixels that
        detected something for one scenario
        """

        # TODO: Add option to specify a different camera direction at each
        # sample point
        CamDir = self.direction
        # Reset the index and set it to T
        allConc = signal.reset_index().set_index('T')

        # Create DataFrame to be returned
        newidx = pd.MultiIndex.from_tuples(sample_points,
                                           names=('T', 'X', 'Y', 'Z'))
        detected_pixels = pd.DataFrame(None, index=newidx,
                                       columns=signal.columns)

        # Check if all the sample times are time points included in the
        # signal dataframe
        signal_t = set(allConc.index)
        sample_t = set([p[0] for p in sample_points])
        if not sample_t.issubset(signal_t):
            raise ValueError('All sampling times for a camera sensor must be '
                             'contained in the signal data')

        # print('    Calculating camera signal detection')
        # TODO: Add interpolation for non-gridded or sparse signal data
        for point in sample_points:
            time = point[0]
            # print('        Time: ', time)
            CamLoc = point[1:]

            # We assume that every sample time is in the signal DataFrame
            # Extract the rows at the sample time
            Conc = allConc.loc[time, :]

            # Might want to move the below calculations to a new function to
            # avoid deeply nested for-loops. Any way to vectorize??

            # No longer need T column
            Conc = Conc.reset_index(drop=True)

            # Set and sort the index so that we can guarantee the order of
            # the rows and use numpy reshape to do the conversion to a 3D array
            Conc = Conc.set_index(['X', 'Y', 'Z'])
            Conc = Conc.sort_index()

            # Get all the unique X, Y, and Z grid points
            gridpoints = list(Conc.index)
            groupedpoints = list(zip(*gridpoints))
            X = np.unique(groupedpoints[0])
            Y = np.unique(groupedpoints[1])
            Z = np.unique(groupedpoints[2])

            # Check if signal is on a regular grid by looking at the number
            # of rows
            nx = len(X)
            ny = len(Y)
            nz = len(Z)

            if nx * ny * nz != Conc.shape[0]:
                raise ValueError('The camera sensor requires signal data '
                                 'to be on a regular grid')

            # Check to make sure X,Y,Z points are equally spaced
            xdiff = np.unique(X[1:] - X[:-1])
            ydiff = np.unique(Y[1:] - Y[:-1])
            zdiff = np.unique(Z[1:] - Z[:-1])
            if len(xdiff) > 1 or len(ydiff) > 1 or len(zdiff) > 1:
                raise ValueError('The camera sensor requires signal data to '
                                 'be equally spaced over a particular '
                                 'spatial axis (i.e. X, Y, and Z)')

            # Calculate angles (horizontal and vertical) associated with the
            # camera orientation. The vertical angle is complemented due to
            # spherical coordinate convention.
            dir1 = np.array(CamDir)
            dir2 = dir1 / (np.sqrt(dir1[0] ** 2 + dir1[1] ** 2 + dir1[2] ** 2))
            horiz = np.arccos(dir2[0])
            vert = np.arccos(dir2[2])

            # The camera has 320 X 240 pixels. To speed up computation, this
            # has been reduced proportionally to 80 X 60. The horizontal (vert)
            # field of view is divided equally among the 80 (60) horizontal
            # (vert) pixels
            # TODO: convert horiz/vert field of view degrees to parameters

            theta_h = np.linspace(horiz - np.pi / 15, horiz + np.pi / 15, 80)
            theta_v = np.linspace(vert - np.pi / 20, vert + np.pi / 20, 60)

            # factor_x, factor_y, factor_z are used later for
            # concentration-pathlength (CPL) calculations. Extrapolation to
            # calculate CPL happens in pixel-coordinates rather than real-life
            # coordinates. 'dist' is the maximum distance that the IR
            # camera can see
            Xstep, Ystep, Zstep = X[1] - X[0], Y[1] - Y[0], Z[1] - Z[0]
            factor_x = int(self.dist / Xstep)
            factor_y = int(self.dist / Ystep)
            factor_z = int((self.dist / 5) / Zstep)

            p, q = len(theta_h), len(theta_v)
            x_end = np.zeros((p, q))
            y_end = np.zeros((p, q))
            z_end = np.zeros((p, q))

            # Calculate the real-life coordinate of a point 'dist' m away for
            # each pixel orientation. This is used to calculate CPL. If 'dist'
            # goes outside the grid boundary the concentration is set to 0.
            for i in range(0, p):
                for j in range(0, q):
                    x_end[i, j] = factor_x * np.cos(theta_h[i]) * \
                                  np.sin(theta_v[j])
                    y_end[i, j] = factor_y * np.sin(theta_h[i]) * \
                                  np.sin(theta_v[j])
                    z_end[i, j] = factor_z * np.cos(theta_v[j])

            # Because calculations happen in pixel coordinates, the
            # location of the camera (start of calculation) and the
            # location of far-away point (end of calculation) is converted
            # to pixel coordinates.
            x_start = (CamLoc[0] - np.min(X)) / Xstep
            y_start = (CamLoc[1] - np.min(Y)) / Ystep
            z_start = (CamLoc[2] - np.min(Z)) / Zstep

            x_end += x_start
            y_end += y_start
            z_end += z_start

            # Calculate camera properties
            nep, tec = self._pixelprop()

            for scen in Conc.columns:
                # Extract the concentration values as a numpy array
                ppm = Conc.loc[:, scen].values
                # Reshape the concentration column as a 3D array
                ppm = ppm.reshape(nx, ny, nz)

                IntConc = np.zeros((p, q))
                CPL = np.zeros((p, q))

                # TODO: Convert this to vector operation to remove for-loops??
                for i in range(0, len(theta_h)):
                    for j in range(0, len(theta_v)):
                        # Calculate concentration pathlength (CPL)
                        IntConc[i, j] = self._pathlength(x_start, y_start,
                                                         z_start,
                                                         x_end[i, j],
                                                         y_end[i, j],
                                                         z_end[i, j], ppm)

                        CPL[i, j] = IntConc[i, j] * self.dist

                # Convert CPL to image contrast and compare it to nep
                # 1e-4 is conversion factor
                attn = CPL * self.Kav * self.NA * 1e-4
                temp = 1 - 10 ** (-attn)
                contrast = temp * np.abs(tec) * self.tau_air

                # Count the number of pixels with a contrast greater than nep
                pixels = sum(sum(contrast >= nep))

                # Camera pixels were truncated to 80 x 60 px, convert pixel
                # count back to the original scale
                pixel_final = 16 * pixels

                detected_pixels.at[point, scen] = pixel_final

        # print(detected_pixels)
        return detected_pixels

    def _pathlength(self, x0, y0, z0, x1, y1, z1, data):
        num = 201  # number of points in extrapolation
        x = np.linspace(x0, x1, num)
        y = np.linspace(y0, y1, num)
        z = np.linspace(z0, z1, num)
        concs = sn.map_coordinates(data, np.vstack((x, y, z)), order=1)
        # CPL as a fraction of total number of points in extrapolation
        avgconc = sum(concs) / num
        return avgconc

    def _pixelprop(self):
        """
        Calculates camera properties

        Returns
        -------
        nep : float
            Noise-equivalent power

        tec : float
            Temperature-emissivity contrast
        """

        T_a = self.T_g - 20

        w1g = self.h * self.c / (self.lambda2 * self.k * self.T_g)
        w2g = self.h * self.c / (self.lambda1 * self.k * self.T_g)
        n1 = 2 * np.pi * self.k ** 4 * self.T_g ** 3 / \
             (self.h ** 3 * self.c ** 2)
        temp_y1 = -np.exp(-w1g) * (720 + 720 * w1g + 360 * w1g ** 2 +
                                   120 * w1g ** 3 + 30 * w1g ** 4 +
                                   6 * w1g ** 5 + w1g ** 6)
        temp_y2 = -np.exp(-w2g) * (720 + 720 * w2g + 360 * w2g ** 2 +
                                   120 * w2g ** 3 + 30 * w2g ** 4 +
                                   6 * w2g ** 5 + w2g ** 6)
        y1 = temp_y2 - temp_y1
        y = y1 * n1
        nep = y * self.netd * self.a_d / (4 * self.f_number ** 2)

        ppixelg = self._pixel_power(self.T_g)
        ppixelp = self._pixel_power(self.T_plume)
        ppixela = self._pixel_power(T_a)

        tec = ppixelp - self.e_g * ppixelg \
              - self.e_a * (1 - self.e_g) * ppixela

        return nep, tec

    def _pixel_power(self, temp):
        """
        Calculates the the power incident on a pixel from an infinite blackbody
        emitter at a given temperature.

        Parameters
        -----------
        temp : float
            Temperature of the emitter (K)

        Returns
        ---------
        Power incident on the pixel (W)
        """

        # Calculate the nondimensional frequency limits of the sensor
        w1 = self.h * self.c / (self.lambda2 * self.k * temp)
        w2 = self.h * self.c / (self.lambda1 * self.k * temp)

        # Integrate the blackbody radiation over the frequency range
        temp_int = integrate.quad(lambda x: x ** 3 / (np.exp(x) - 1), w1, w2)

        # calculate the power incident on one camera pixel
        frac = temp_int[0] / (np.pi ** 4 / 15)
        sblaw = self.SIGMA * temp ** 4 * self.a_d
        power = (4 / np.pi) * sblaw * np.tan(self.fov1 / 2) * \
                np.tan(self.fov2 / 2)
        pixel_power = power * frac
        return pixel_power


# -*- coding: utf-8 -*-
"""
Created on Fri Apr  5 19:05:53 2019

@author: User
"""

# -*- coding: utf-8 -*-
"""
Created on Tue Mar 19 15:25:50 2019

@author: Joan

This program adds all the words in an Amazon review to 2 dictionaries (Good and Bad).

"""
import time
import csv
import json
import re
from collections import Counter, OrderedDict
from nltk.corpus import stopwords
import all_clean_funcs
import operator
from textblob import TextBlob
import emoji

name_of_bad = str(input('Enter file name for bad dictionary:'))
name_of_good = str(input('Enter file name for good dictionary:'))

time_start = time.time()

good = []
bad = []
c=0

with open('all_out.csv', mode = 'r', encoding = 'utf8') as infile:
    reader = csv.reader(infile)
    for rows in reader:
        print(c)
        # --- STRING --- #
        # Remove all punctuation and symbols
        no_punct = remove_symbols(rows[5])

        # Remove emojis
        emojidict = {}
        no_punct_emoji = extractemoji(no_punct, emojidict)

        # --- LIST OF WORDS ---#
        # Split string of review into list of words as elements
        #no_punct_split = no_punct.split()

        # Remove prepositions, coordinating conjunctions,
        # cardinal number, determiner, to
        grammar_cleaned, grammar_removed = remove_grammar_words(no_punct_emoji)

        # Remove stopwords
        filtered_sentence = remove_stop_words(grammar_cleaned)

        # Put words in either good or bad dicts
        if rows[6] == '1.0 out of 5 stars' or rows[6] == '2.0 out of 5 stars' or rows[6] == '3.0 out of 5 stars':
            bad.extend(filtered_sentence)
        else:
            good.extend(filtered_sentence)
        c+=1

    # Count frequencies of words in dicts
    bad_dict = dict(Counter(bad))
    good_dict = dict(Counter(good))

    # Remove words that appear in both dicts
    cle_bad_dict, cle_good_dict = clean(bad_dict, good_dict, 0.3)

    # Sort dicts by descending value
    sort_bad_dict, sort_good_dict = sort_dict_desc(cle_bad_dict, cle_good_dict)

    print('Number of pairs in bad dict:', len(sort_bad_dict))
    print('\nNumber of pairs in good dict:', len(sort_good_dict))

    print(sort_bad_dict)

save_dicts(name_of_bad, name_of_good, sort_bad_dict, sort_good_dict)

time_elapsed = time.time() - time_start

print('Time Elapsed:', time_elapsed)


import math

import cv2
import time
import numpy
import numpy as np

import HandsTracking as ht

from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume

videocap=cv2.VideoCapture(0)

prevtime=0

detector=ht.HandDetection()

devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(
    IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = cast(interface, POINTER(IAudioEndpointVolume))
#volume.GetMute()
#volume.GetMasterVolumeLevel()
volrange=volume.GetVolumeRange()  #-65 to 0

minvol=volrange[0]
maxvol=volrange[1]

while True:
    test, imgframe=videocap.read()
    img=imgframe
    #img=detector.Hands(imgframe)
    lmlist=detector.Location(img,draw=False)
    if len(lmlist)>0:
        #print(lmlist[4],lmlist[8])
        x1,y1=lmlist[4][0],lmlist[4][1]
        x2, y2 = lmlist[8][0], lmlist[8][1]
        cv2.rectangle(img,(x1-5,y1-5),(x1+5,y1+5),cv2.COLOR_YUV420sp2GRAY,10)
        cv2.rectangle(img, (x2 - 5, y2 - 5), (x2 + 5, y2 + 5), cv2.COLOR_YUV420sp2GRAY, 10)
        cv2.line(img,(x1,y1),(x2,y2),(202,122,0),3)
        cx,cy=(x1+x2)//2,(y1+y2)//2
        cv2.circle(img, (cx, cy), 10, (33, 112, 234), cv2.FILLED)

        l=math.hypot(x1-x2,y1-y2)
        #print(l)

        # Length range 40-290
        vol=np.interp(l,[30,270],[minvol,maxvol])
        volume.SetMasterVolumeLevel(vol, None)
        print(vol)

        if l<50:
            cv2.circle(img, (cx, cy), 10, (0, 255, 0), cv2.FILLED)

    img=cv2.resize(img,(800,500))
    cv2.imshow("Img", img)
    cv2.waitKey(1)


from botocore.exceptions import BotoCoreError, ClientError
from fastapi.requests import Request
from fastapi.responses import JSONResponse
from sqlalchemy.exc import DBAPIError, SQLAlchemyError

from .crud import database_logger

class NoSuchBucket(Exception):
    """Custom exception that will be raised if minio bucket doesn't exist."""

    def __init__(self, message: str):
        self.message = message

def sqlalchemy_exception_handler(
    request: Request, error: SQLAlchemyError
) -> JSONResponse:
    database_logger.error(error)
    return JSONResponse(
        status_code=500,
        content={"detail": f"Error: SQLAlchemy error ({error})"},
    )

def dbapi_exception_handler(
    request: Request, error: DBAPIError
) -> JSONResponse:
    database_logger.error(error.__cause__)
    return JSONResponse(
        status_code=500,
        content={"detail": f"Error: DBAPI error ({error.__cause__})"},
    )

def no_such_bucket_error_handler(
    request: Request, exc: NoSuchBucket
) -> JSONResponse:
    return JSONResponse(
        status_code=404,
        content={"detail": f"Error: {exc.message}"},
    )

def botocore_error_handler(
    request: Request, exc: BotoCoreError
) -> JSONResponse:
    return JSONResponse(
        status_code=500,
        content={"detail": f"Error: {exc}"},
    )

def minio_client_error_handler(
    request: Request, exc: ClientError
) -> JSONResponse:
    return JSONResponse(
        status_code=500,
        content={"detail": f"Error: {exc}"},
    )


from django.urls import path
from . import views

app_name = 'blog'
urlpatterns = [
    path('', views.post_list, name='post_list'),
    # path('', views.PostListView.as_view(), name='post_list'),
    path('<int:year>/<int:month>/<int:day>/<slug:post>/', views.post_detail, name='post_detail'),
    path('category/<int:pk>/', views.category_list, name='category_list'),
    path('tag/<int:pk>/', views.tag_list, name='tag_list'),

    path('register/', views.register, name='register'),
    path('login/', views.login, name='login'),
]


from Volley.genetics import SuperGene
import matplotlib.pyplot as plt

class FitnessFunction:
    def __init__(self, function, super_genes: list[SuperGene], start_kill=1, kill_percent_reduce = .1):
        self.start_kill = start_kill
        self.function = function
        self.super_genes = super_genes
        self.kill_percent_reduce = kill_percent_reduce
        self.epoch = 0
        self.history = []

    def fit(self):
        data = [super_gene.current_gene for super_gene in self.super_genes]
        fitness = self.function(data)
        self.history.append(fitness)
        self.epoch += 1
        for super_gene in self.super_genes:
            super_gene.current_gene.fit(fitness)
        if self.epoch >= self.start_kill:
            for super_gene in self.super_genes:
                super_gene.check(self.kill_percent_reduce)
        return fitness

    def shuffle_all(self):
        for gene in self.super_genes:
            gene.shuffle()

    def plot(self):
        plt.plot(self.history)
        plt.show()


#Importa a biblioteca
import folium

#Cria o mapa base
map = folium.Map(location=[-20.807382,-49.360453], zoom_start = 10)

#Multiplos marcadores
for coordinates in [[-20.800385, -49.367797],[-20.800154, -49.368665]]:
    folium.Marker(location=coordinates, icon=folium.Icon(color = 'green')).add_to(map)

#Salva o mapa
map.save("mapa.html")


from fastapi import APIRouter, HTTPException, Depends
from utils.mongo_utils import register_user
from utils.enums import Register
from validations.register_validator import UserRegistrationRequest
from utils.rate_limiter import register_limiter, init_rate_limiter

router = APIRouter()

@router.post("/user-register", status_code=201, dependencies=[Depends(register_limiter)])
async def register_user_endpoint(user_data: UserRegistrationRequest):
    user_data_dict = user_data.dict()
    registration_status = register_user(user_data_dict)
    if(registration_status == "USERNAME_EXISTS"):
        raise HTTPException(status_code=400, detail="Username already exists")
    elif(registration_status == "EMAIL_EXISTS"):
        raise HTTPException(status_code=400, detail="Email already exists")
    elif(registration_status == "REGISTRATION_SUCCESS"):
        return {"message": "User registration successful"}
    else:
        raise HTTPException(status_code=500, detail="Someting went wrong")

    # if not register_user(user_data_dict):
    #     raise HTTPException(status_code=400, detail="Username or email already exists")

    # return {"message": "User registration successful"}


from typing import Tuple
import click
from pipelime.pipes.piper import Piper, PiperCommand

@click.command("smart_converter", help="Converts Subfolders to Underfolder")
@click.option("-i", "--input_folder", required=True, type=str, help="Input H5 Filename")
@click.option(
    "-o", "--output_folder", required=True, type=str, help="Output Underfolder"
)
@click.option(
    "-e",
    "--extensions_map",
    required=True,
    type=(str, str),
    multiple=True,
    help="Image extensions map pairs {item_name:extension} as multiple tuples",
)
@Piper.command(
    inputs=["input_folder"],
    outputs=["output_folder"],
)
def smart_converter(
    input_folder: str,
    output_folder: str,
    extensions_map: str,
):

    import rich

    from pipelime.converters.smartconverter import SmartConverter

    extensions_map = {a: b for a, b in extensions_map}
    converter = SmartConverter(
        folder=input_folder,
        extensions_map=extensions_map,
    )
    converter.convert(output_folder)
    rich.print("Underfolder Writer output to:", output_folder)


from typing import List

class Solution:
    def flipAndInvertImage(self, image: List[List[int]]) -> List[List[int]]:
        row_size = len(image)
        col_size = len(image[0])

        for r in range(row_size):
            for c in range(col_size // 2):
                image[r][c], image[r][col_size - c - 1] = image[r][col_size - c - 1], image[r][c]

        for r in range(row_size):
            for c in range(col_size):
                image[r][c] = 0 if image[r][c] else 1

        return image

def main():
    sol = Solution()
    print(sol.flipAndInvertImage([[1,1,0],[1,0,1],[0,0,0]]))
    print(sol.flipAndInvertImage([[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]))

if __name__ == '__main__':
    main()


from django.db import models
from cloudinary.models import CloudinaryField

class Category(models.Model):
    class Meta(object):
        db_table = 'category'
        verbose_name_plural = "Categories"

    name = models.CharField(
        'Name', blank=False, null=False, max_length=200, db_index=True
    )
    image = CloudinaryField(
        "Category Image", blank=True, null=True
    )
    created_at = models.DateTimeField(
        'Creation Date', blank=True, auto_now_add=True
    )
    updated_at = models.DateTimeField(
        'Update Date', blank=True, auto_now=True
    )

    def __str__(self):
        return self.name


import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.gridspec as gridspec

def setup_axes(m=4, n=5, colorbar=True, title='', figsize=(15, 7)):

    fig = plt.figure(figsize=figsize)

    gs = gridspec.GridSpec(m, n, left=0.05, bottom=0.1, top=0.95, right=0.98,
            figure=fig, wspace=0.0, hspace=0.0)

    axes = []
    for bi in range(19):
        i = int(bi // n)
        j = int(bi - i * n)
        ax = fig.add_subplot(gs[i,j])

        if i != m-1:
            ax.set_xticklabels([])
        if j != 0:
            ax.set_yticklabels([])

        axes.append(ax)

    if colorbar:
        if m == 5:
            cax = fig.add_axes([0.85, 0.17, 0.19, 0.02])
        else:
            cax = fig.add_axes([0.84/float(m) * (m - 1) + 0.16, 0.14, 0.19, 0.02])
        axes.append(cax)
    else:
        if m == 5:
            cax = fig.add_axes([0.75, 0.17, 0.19, 0.02])
        else:
            cax = fig.add_axes([0.84/float(m) * (m - 1) + 0.14, 0.14, 0.19, 0.02])
        #cax.get_xaxis().set_visible(False)
        #cax.get_yaxis().set_visible(False)
        cax.set_axis_off()
        cax.set_title(title)

    return fig, axes


#   Programmer: limodou
#   E-mail:     limodou@gmail.coms
#
#   Copyleft 2006 limodou
#
#   Distributed under the terms of the GPL (GNU Public License)
#
#   UliPad is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#   $Id: mDirBrowser.py 1897 2007-02-03 10:33:43Z limodou $

import wx
import os
import sys
from modules import Mixin
from modules import Globals

def add_tool_list(toollist, toolbaritems):
    toollist.extend([
        (115, 'dir'),
    ])

    #order, IDname, imagefile, short text, long text, func
    toolbaritems.update({
        'dir':(wx.ITEM_CHECK, 'IDM_WINDOW_DIRBROWSER', 'images/dir.gif', tr('Directory Browser'), tr('Shows the Directory Browser pane.'), 'OnWindowDirBrowser'),
    })
Mixin.setPlugin('mainframe', 'add_tool_list', add_tool_list)

def afterinit(win):
    wx.EVT_UPDATE_UI(win, win.IDM_WINDOW_DIRBROWSER, win.OnUpdateUI)
Mixin.setPlugin('mainframe', 'afterinit', afterinit)

_dirbrowser_pagename = tr('Directory Browser')

def on_mainframe_updateui(win, event):
    eid = event.GetId()
    if eid == win.IDM_WINDOW_DIRBROWSER:
        page = win.panel.getPage(_dirbrowser_pagename)
        event.Check(bool(page) and win.panel.LeftIsVisible)
Mixin.setPlugin('mainframe', 'on_update_ui', on_mainframe_updateui)

def add_mainframe_menu(menulist):
    menulist.extend([('IDM_FILE',
        [
            (138, 'IDM_WINDOW_DIRBROWSER', tr('Directory Browser')+'\tF2', wx.ITEM_CHECK, 'OnWindowDirBrowser', tr('Shows the Directory Browser pane.'))
        ]),
    ])
Mixin.setPlugin('mainframe', 'add_menu', add_mainframe_menu)

def add_notebook_menu(popmenulist):
    popmenulist.extend([(None,
        [
            (170, 'IDPM_DIRBROWSERWINDOW', tr('Directory Browser'), wx.ITEM_NORMAL, 'OnDirBrowserWindow', tr('Shows the Directory Browser pane.')),
        ]),
    ])
Mixin.setPlugin('notebook', 'add_menu', add_notebook_menu)

def on_notebook_updateui(win, event):
    eid = event.GetId()
    if eid == win.IDPM_DIRBROWSERWINDOW:
        event.Check(bool(Globals.mainframe.panel.getPage(tr('Directory Browser'))) and win.panel.LeftIsVisible)
Mixin.setPlugin('notebook', 'on_update_ui', on_notebook_updateui)

def init(win):
    wx.EVT_UPDATE_UI(win, win.IDPM_DIRBROWSERWINDOW, win.OnUpdateUI)
Mixin.setPlugin('notebook', 'init', init)

def afterinit(win):
    win.dirbrowser_imagelist = {
        'close':'images/folderclose.gif',
        'open':'images/folderopen.gif',
        'item':'images/file.gif',
    }
    if win.pref.open_last_dir_as_startup and win.pref.last_dir_paths:
        wx.CallAfter(win.createDirBrowserWindow, win.pref.last_dir_paths)
        wx.CallAfter(win.panel.showPage, _dirbrowser_pagename)
Mixin.setPlugin('mainframe', 'afterinit', afterinit)

def createDirBrowserWindow(win, dirs=None):
    page = None
    if not win.panel.getPage(_dirbrowser_pagename):
        from DirBrowser import DirBrowser

        if not dirs:
            dirs = win.pref.last_dir_paths
        page = DirBrowser(win.panel.createNotebook('left'), win, dirs)
        win.panel.addPage('left', page, _dirbrowser_pagename)
    return page
Mixin.setMixin('mainframe', 'createDirBrowserWindow', createDirBrowserWindow)

def toggleDirBrowserWindow(win):
    page = win.panel.getPage(_dirbrowser_pagename)
    if page:
        win.panel.closePage(_dirbrowser_pagename)
    else:
        if win.createDirBrowserWindow():
            win.panel.showPage(_dirbrowser_pagename)
Mixin.setMixin('mainframe', 'toggleDirBrowserWindow', toggleDirBrowserWindow)

def OnWindowDirBrowser(win, event):
    win.toggleDirBrowserWindow()
Mixin.setMixin('mainframe', 'OnWindowDirBrowser', OnWindowDirBrowser)

def OnDirBrowserWindow(win, event):
    win.mainframe.toggleDirBrowserWindow()
Mixin.setMixin('notebook', 'OnDirBrowserWindow', OnDirBrowserWindow)

def pref_init(pref):
    pref.recent_dir_paths = []
    pref.recent_dir_paths_num = 20
    pref.last_dir_paths = []
    pref.open_last_dir_as_startup = True
    pref.dirbrowser_last_addpath = os.getcwd()
    if sys.platform == 'win32':
        cmdline = os.environ['ComSpec']
        pref.command_line = cmdline
    else:
        pref.command_line = 'gnome-terminal --working-directory={path}'
    pref.open_project_setting_dlg = True
Mixin.setPlugin('preference', 'init', pref_init)

def add_pref(preflist):
    preflist.extend([
#        (tr('General'), 100, 'num', 'recent_dir_paths_num', tr('Max number of recent browse directories:'), None),
        (tr('General'), 150, 'check', 'open_last_dir_as_startup', tr('Open the last directory at startup'), None),
        (tr('General'), 151, 'check', 'open_project_setting_dlg', tr('Open the Project Settings dialog if a directory is added to the Directory Browser'), None),
        (tr('General'), 160, 'openfile', 'command_line', tr('Command line of Open Command Window Here:'), {'span':True}),
    ])
Mixin.setPlugin('preference', 'add_pref', add_pref)

def after_addpath(dirbrowser, node):
    Globals.mainframe.pref.last_dir_paths = dirbrowser.getTopDirs()
    Globals.mainframe.pref.save()
Mixin.setPlugin('dirbrowser', 'after_addpath', after_addpath)

def after_closepath(dirbrowser, path):
    Globals.mainframe.pref.last_dir_paths = dirbrowser.getTopDirs()
    Globals.mainframe.pref.save()
Mixin.setPlugin('dirbrowser', 'after_closepath', after_closepath)

def afterclosewindow(win):
    win.panel.showWindow('LEFT', False)
    win.panel.showWindow('bottom', False)
Mixin.setPlugin('mainframe', 'afterclosewindow', afterclosewindow)


import datetime
import os
import time
import cv2
import ffmpeg
import numpy as np

# filepath = "vtest.avi"
# cap = cv2.VideoCapture(filepath)
# Webカメラを使うときはこちら
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

avg = None

_, frame = cap.read()

# 動体検知参考資料
#  https://qiita.com/KMiura95/items/4eed79a7da6b3dafa96d
# ffmpeg参考資料
#  https://qiita.com/mitayuki6/items/73943628b625e0b2ab30

contours_area_threshold = 90000
t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
video_path = None
initial_not_detect_time = None
video_file_divide_sec = 10
while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break

    # グレースケールに変換
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 比較用のフレームを取得する
    if avg is None:
        avg = gray.copy().astype("float")
        continue

    # 現在のフレームと移動平均との差を計算
    cv2.accumulateWeighted(gray, avg, 0.6)
    frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))

    # デルタ画像を閾値処理を行う
    thresh = cv2.threshold(frameDelta, 3, 255, cv2.THRESH_BINARY)[1]
    # 画像の閾値に輪郭線を入れる
    contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours2 = list(filter(lambda x: cv2.contourArea(x) >= contours_area_threshold, contours))
    # contoured_frame = cv2.drawContours(frame, contours2, -1, (0, 255, 0), 3)
    if len(contours2) == 0 and video_path is not None:
        now = time.time()
        if initial_not_detect_time is None:
            initial_not_detect_time = now
        elif now - initial_not_detect_time > video_file_divide_sec:
            video_path = None
            initial_not_detect_time = None
            try:
                process.stdin.close()
                process.wait()
            except:
                pass
    elif len(contours2) > 0 and video_path is None:
        initial_not_detect_time = None
        now = datetime.datetime.now(JST)
        video_path = now.strftime('img/%Y/%m/%d/%H-%M-%S.%f.avi')
        video_dir = os.path.dirname(video_path)
        if not os.path.exists(video_dir):
            os.makedirs(video_dir)
        height, width = frame.shape[:2]
        # 可変フレームレートで動画ファイルとして書き出すためffmpegプロセス
        process = (
            ffmpeg.input(
                'pipe:', format='rawvideo', pix_fmt='bgr24',
                s='{}x{}'.format(width, height), use_wallclock_as_timestamps=1).output(
                    video_path, vsync='vfr', r=24.0).overwrite_output().run_async(pipe_stdin=True)
        )
        print("Detected, path %s" % video_path)
    elif len(contours2) > 0:
        initial_not_detect_time = None

    # ビデオ保存
    if video_path is not None:
        process.stdin.write(frame.astype(np.uint8).tobytes())

process.stdin.close()
process.wait()

cap.release()


import numpy as np
import time
import torch
import torch.nn as nn
import torch.optim as optim

from .expt_template import Base
from .utilities import MaxPool1dFixed

device = "cuda" if torch.cuda.is_available() else "cpu"

class MLP_1_hidden(nn.Module):
    def __init__(self, input_size = 1024, hidden_size = 512):
        super().__init__()
        self.input_size = input_size
        self.hidden_size  = hidden_size
        self.fc1 = torch.nn.Linear(self.input_size, self.hidden_size)
        self.out = torch.nn.Linear(self.hidden_size, self.hidden_size)
        self.logit_scale = nn.Parameter(torch.ones([], device=device))

    def forward(self, input):
        output = self.out(self.fc1(input))
        return output

class MLPExpt(Base):
    def __init__(self,variant="MLP_1_hidden", n_shot=5, lr=0.003):

        super().__init__()

        if variant == "MLP_1_hidden":
            self.model = MLP_1_hidden().to(device)
        self.optimizer = torch.optim.SGD(self.model.parameters(), lr=lr, weight_decay=1e-5)
        self.criterion = nn.CrossEntropyLoss(reduction="sum")  # to make calculation of loss and accuracy comparable
        self.scheduler = optim.lr_scheduler.MultiStepLR(
            self.optimizer, milestones=[150, 250, 350], gamma=0.1
        )
        self.cossim = torch.nn.CosineSimilarity()
        self.softmax = torch.nn.Softmax(dim=1)
        self.max_pool = MaxPool1dFixed(n_shot)

    def calc_loss(self, prototypes, support_labels, queries, query_labels, reduction="max"):
        """
        prototypes: NWAY*NSHOT, 512
        queries: NWAY*NQueries, 512
        query_labels: NWAY*NQueries
        """
        logit_scale = self.model.logit_scale.exp()
        logits_per_image = logit_scale * queries @ prototypes.t() # NWAY*NQueries, NWAY*NSHOT
        probabilities = self.softmax(logits_per_image)

        #Assuming that relative sorting of both support labels are grouped by label, hence maxpooling makes sense
        assert all(torch.eq(support_labels,torch.sort(support_labels)[0]))

        probabilities_collapsed = self.max_pool(probabilities)

        return self.criterion(probabilities_collapsed, query_labels)

    def num_correct_preds(self, prototypes, support_labels, queries, query_labels, reduction="max"):
        probabilities = queries @ prototypes.t()
        probabilities_collapsed = self.softmax(self.max_pool(probabilities))
        return torch.sum(probabilities_collapsed.argmax(dim=1) == query_labels).item()

    def train_loop(self, train_loader, epoch, class_prototype_calculation="max"):

        """

        class_prototype_calculation: Defines method by which we will be calculating loss/accuracy
            "max": take argmax of all prototypes and predict for corresponding label
            "mean": takes mean of all the prototypes

        Doesn't use support labels because they have already been used in Task Sampler to generate the appended img text embedding
        """

        train_loss = 0.0
        total = 0
        correct = 0

        start_time = time.time()
        for i, (
            support_inputs, # N_WAY x N_SHOT , 1024 - tensor
            support_labels, # N_WAY x N_SHOT , 1 - tensor
            query_inputs, # N_WAY x N_QUERY , 1024 - tensor
            query_labels, # N_WAY x N_QUERY , 1 - tensor
            true_class_ids, # N_WAY  - list
        ) in enumerate(train_loader):

            support_inputs = support_inputs.to(device)
            support_labels = support_labels.to(device)
            query_inputs = query_inputs.to(device)
            query_labels = query_labels.to(device)

            support_labels_sorted, indices = torch.sort(support_labels)
            support_inputs_sorted = support_inputs[indices]

            self.model.train()
            loss= 0

            self.optimizer.zero_grad()

            class_prototype_predictions = self.model(support_inputs_sorted) # N_WAY x N_SHOT, 512

            query_image_embs = query_inputs[:,:512]
            loss = self.calc_loss(class_prototype_predictions, support_labels_sorted, query_image_embs, query_labels.cuda())
            loss.backward()
            self.optimizer.step()
            train_loss += loss.item()
            total += query_labels.size(0)

            corr = self.num_correct_preds(class_prototype_predictions, support_labels_sorted, query_image_embs, query_labels.cuda())
            correct += corr

            print(i, loss.item()/query_labels.size(0), corr/query_labels.size(0))

        self.scheduler.step()
        epoch_loss = train_loss/total
        epoch_accuracy = correct*100/total
        return (epoch_loss, epoch_accuracy)

    def evaluate(self, test_loader):

        for i, (
            support_inputs, # N_WAY x N_SHOT , 1024 - tensor
            support_labels, # N_WAY x N_SHOT , 1 - tensor
            query_inputs, # N_WAY x N_QUERY , 1024 - tensor
            query_labels, # N_WAY x N_QUERY , 1 - tensor
            true_class_ids, # N_WAY  - list
        ) in enumerate(test_loader):

            with torch.no_grad():

                # train_labels_sorted, indices = torch.sort(train_labels)
                # train_inputs_sorted = train_embeddings[indices]

                support_labels_sorted, indices = torch.sort(support_labels)
                support_inputs_sorted = support_inputs[indices]

                self.model.eval()
                loss= 0

                class_prototype_predictions = self.model(support_inputs_sorted)  # N_WAY x N_SHOT, 512

                test_image_embs = query_inputs[:,:512]
                loss = self.calc_loss(class_prototype_predictions, support_labels_sorted, test_image_embs, query_labels.cuda())
                test_loss = loss.item()
                total = query_labels.size(0)

                corr = self.num_correct_preds(class_prototype_predictions, support_labels_sorted, test_image_embs, query_labels.cuda())

                epoch_loss = test_loss/total
                epoch_accuracy = corr*100/total
                print(
                    f"Accuracy: {epoch_accuracy:7.3f}%"
                )
                return (epoch_loss, epoch_accuracy)


import pandas as pd
from car import Car
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import *
from utils import only_numbers
from xpath import Xpath

def main():
    xpath=Xpath()
    webd_path = 'chromedriver.exe'

    driver = webdriver.Chrome(webd_path)

    driver.get('https://cars.skoda-auto.com/456/pl-pl/carSearch')

    driver.implicitly_wait(30)

    accept_button = driver.find_element(By.ID, 'onetrust-accept-btn-handler')
    accept_button.click()
    del accept_button

    cars=[]
    number_of_car=0
    # repair the link
    for a in range(1,3):
        for n in range(6):
            car_element = driver.find_element(By.CSS_SELECTOR,(f"#car-search-item-{number_of_car}"))
            number_of_car+=1

            # getting all info
            car_element.click()

            car=Car()
            # gettin all of the car info
            car.link = driver.current_url
            car.model=driver.find_element(By.XPATH, xpath.carmodel).text
            car.engine=driver.find_element(By.XPATH,xpath.carengine).text
            car.price=only_numbers(driver.find_element(By.XPATH,xpath.carprice).text)
            car.installment=only_numbers(driver.find_element(By.XPATH,xpath.carinstallment).text)
            car.prod_year=driver.find_element(By.XPATH,xpath.carprod_year).text
            car.vin=driver.find_element(By.XPATH,xpath.carvin).text
            car.color=driver.find_element(By.XPATH,xpath.carcolor).text
            cars.append(car)

            driver.get('https://cars.skoda-auto.com/456/pl-pl/carSearch')
            driver.implicitly_wait(10)
            if a%6>0:
                for more_page_results in range(a%6):
                    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
                    driver.find_element(By.ID,"result-page-more-results-btn").click()
                    driver.implicitly_wait(10)
                    driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

    # sleep (11)
    carlinks=[]
    carmodels=[]
    carengines=[]
    carprices=[]
    carinstallments=[]
    carprod_years=[]
    carvins=[]
    carcolors=[]
    inf=[carlinks,carmodels,carengines,carprices,carinstallments,carprod_years,carvins,carcolors]

    for car in cars:
        inf=car.car_info(inf)

    cardic={"link":inf[0],"model":inf[1],"engine":inf[2],"price":inf[3],"installment":inf[4],"prod_year":inf[5],"vin":inf[6],"color":inf[7]}
    cardf=pd.DataFrame(cardic)
    print(cardf)

    cardf.to_excel("car_excel.xlsx")

    # try:
    #     SCROLL_PAUSA_TIME = 3
    #     driver.get(subcategory_url)
    #     last_height = driver.execute_script("return document.body.scrollHeight")
    #     while True:
    #         driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
    #         time.sleep(SCROLL_PAUSA_TIME)
    #         new_height = driver.execute_script("return document.body.scrollHeight")
    #         if new_height == last_height:
    #             time.sleep(3)
    #             break
    #         last_height = new_height

 # .get_attribute('href')

    # driver.close()

if __name__ == '__main__':
    main()


from typing import List, Optional, Tuple, Iterable
from dataclasses import dataclass

__pdoc__ = {
    "EntitieErrorInfo.__init__": False,
    "GetEntitiesError.__init__": False,
}

@dataclass(init=False)
class EntityErrorInfo:
    """
    Represents a failed entity retrieval.
    """

    __slots__ = ("name", "error_message")

    name: str
    error_message: str

    @property
    def is_error(self) -> bool:
        """
        Always returns True since this instance always indicates an error.
        """
        return True

    def __init__(self, name: str, error_message: str):
        self.name = name
        """The name of the entity."""

        self.error_message = error_message
        """Contains the error message."""

@dataclass(init=False)
class GetEntitiesError(Exception):
    entities: List[EntityErrorInfo]
    message: str

    def __init__(self, entities: List[EntityErrorInfo]):
        self.entities = entities
        """entities"""

        self.message = "failed to retrieve:\n" + (
            "\n".join("\t" + x.name + " error_message: " + x.error_message for x in self.entities)
        )
        """message"""

        super().__init__(self.message)

    @classmethod
    def _raise_if(cls, entities: Iterable[Tuple[str, Optional[str]]]) -> None:
        entities_list = [EntityErrorInfo(x, y) for x, y in entities if y]
        if entities_list:
            raise GetEntitiesError(entities_list)