import copy
import functools
import logging

import matplotlib.pyplot as plt
import numpy as np
import pyqtgraph as pg
from PyQt4.QtCore import Qt, QSize, QEvent, QMetaObject
from PyQt4.QtCore import pyqtSignal
from PyQt4.QtGui import QTableView, QWidget, QVBoxLayout, QHBoxLayout, \
    QSizePolicy, QPushButton, QSpacerItem, QApplication, QDockWidget,\
    QComboBox, QColor, QMessageBox
from qgis.core import QgsGeometry, QgsCoordinateTransform
from qgis.core import QgsFeatureRequest

from zDeltaresTdiToolbox.config.waterbalance.sum_configs import serie_settings
from zDeltaresTdiToolbox.models.wb_item import WaterbalanceItemModel
from zDeltaresTdiToolbox.utils.maptools.polygon_draw import PolygonDrawTool

log = logging.getLogger('DeltaresTdi.' + __name__)

try:
    _encoding = QApplication.UnicodeUTF8

    def _translate(context, text, disambig):
        return QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QApplication.translate(context, text, disambig)

serie_settings = {s['name']: s for s in serie_settings}

INPUT_SERIES = [
    ('2d_in', 0, '2d'),
    ('2d_out', 1, '2d'),
    ('1d_in', 2, '1d'),
    ('1d_out', 3, '1d'),
    ('2d_bound_in', 4, '2d'),
    ('2d_bound_out', 5, '2d'),
    ('1d_bound_in', 6, '1d'),
    ('1d_bound_out', 7, '1d'),
    ('1d_2d_in', 8, '1d'),
    ('1d_2d_out', 9, '2d'),
    ('2d_to_1d_pos', 10, '1d_2d'),
    ('2d_to_1d_neg', 11, '1d_2d'),
    ('pump_in', 12, '1d'),
    ('pump_out', 13, '1d'),
    ('rain', 14, '2d'),
    ('infiltration_rate_simple', 15, '2d'),
    ('lat_2d', 16, '2d'),
    ('lat_1d', 17, '1d'),
    ('d_2d_vol', 18, '2d'),
    ('d_1d_vol', 19, '1d'),
    ('error_2d', 20, 'error_2d'),
    ('error_1d', 21, 'error_1d'),
    ('error_1d_2d', 22, 'error_1d_2d'),
    ('2d_groundwater_in', 23, '2d'),
    ('2d_groundwater_out', 24, '2d'),
    ('d_2d_groundwater_vol', 25, '2d'),
    ('leak', 26, '2d'),
    ('inflow', 27, '1d'),
    ('2d_vertical_infiltration_pos', 28, '2d_vert'),
    ('2d_vertical_infiltration_neg', 29, '2d_vert'),
]

# some helper functions
#######################

def _get_request_filter(ids):
    ids_flat = list(set([i for j in ids.values() for i in j]))
    return QgsFeatureRequest().setFilterFids(ids_flat)

def _get_feature_iterator(layer, request_filter):
    # mainly pumps are often not present
    if layer:
        return layer.getFeatures(request_filter)
    else:
        return []

#######################

@functools.total_ordering
class Bar(object):
    """Bar for waterbalance barchart with positive and negative components.
    """
    SERIES_NAME_TO_INDEX = {name: idx for (name, idx, _) in INPUT_SERIES}

    def __init__(self, label_name, in_series, out_series, type):
        self.label_name = label_name
        self.in_series = in_series
        self.out_series = out_series
        self.type = type
        self._balance_in = None
        self._balance_out = None

    @staticmethod
    def _get_time_indices(ts, t1, t2):
        """Time series indices in range t1-t2."""
        idx_x1 = np.searchsorted(ts, t1)
        if not t2:
            idx_x2 = len(ts)
        else:
            idx_x2 = np.searchsorted(ts, t2)
        return np.arange(idx_x1, idx_x2)

    @property
    def end_balance_in(self):
        return self._balance_in

    def set_end_balance_in(self, ts, ts_series, t1=0, t2=None):
        idxs = [self.SERIES_NAME_TO_INDEX[name] for name in self.in_series]
        ts_indices_sliced = self._get_time_indices(ts, t1, t2)
        # NOTE: we're using np.clip to determine in/out for dvol (for flows
        # /discharges this shouldn't matter I THINK)
        ts_deltas = np.concatenate(([0], np.diff(ts)))
        # shape = (N_idxs, len(ts))
        balance_tmp = (ts_deltas * ts_series[:, idxs].T).clip(min=0)
        self._balance_in = balance_tmp[:, ts_indices_sliced].sum()

    @property
    def end_balance_out(self):
        return self._balance_out

    def set_end_balance_out(self, ts, ts_series, t1=0, t2=None):
        idxs = [self.SERIES_NAME_TO_INDEX[name] for name in self.out_series]
        ts_indices_sliced = self._get_time_indices(ts, t1, t2)
        # NOTE: we're using np.clip to determine in/out for dvol (for flows
        # /discharges this shouldn't matter I THINK)
        ts_deltas = np.concatenate(([0], np.diff(ts)))
        # shape = (N_idxs, len(ts))
        balance_tmp = (ts_deltas * ts_series[:, idxs].T).clip(max=0)
        self._balance_out = balance_tmp[:, ts_indices_sliced].sum()

    def calc_balance(self, ts, ts_series, t1=0, t2=None):
        """Calculate balance values."""
        self.set_end_balance_in(ts, ts_series, t1, t2)
        self.set_end_balance_out(ts, ts_series, t1, t2)
        if self.is_storage_like:
            self.convert_to_net()

    def convert_to_net(self):
        """Make a bar that contains the net value (positive or negative).
        """
        # NOTE: use addition because out is negative
        net_val = self._balance_in + self._balance_out
        if net_val > 0:
            self._balance_in = net_val
            self._balance_out = 0
        else:
            self._balance_in = 0
            self._balance_out = net_val

    def invert(self):
        """Flip positive to negative and vice versa."""
        self._balance_in, self._balance_out = \
            -1 * self._balance_out, -1 * self._balance_in

    @property
    def is_storage_like(self):
        return 'storage' in self.label_name

    # add sorting
    def __lt__(self, other):
        # TODO: label_names are not unique, should add 'type' to make a
        # primary key
        if not self.is_storage_like and other.is_storage_like:
            return True
        elif self.is_storage_like and not other.is_storage_like:
            return False
        return self.label_name < other.label_name

class BarManager(object):
    def __init__(self, series):
        self.series = series
        self.bars = sorted([
            Bar(
                label_name=x['label_name'],
                in_series=x['in'],
                out_series=x['out'],
                type=x['type'],
            ) for x in series
        ])

    def calc_balance(
            self, ts, ts_series, t1, t2, net=False, invert=[]):
        for b in self.bars:
            b.calc_balance(ts, ts_series, t1=t1, t2=t2)
            if net:
                b.convert_to_net()
            if b.label_name in invert:
                b.invert()

    @property
    def x(self):
        return np.arange(len(self.bars))

    @property
    def xlabels(self):
        return [b.label_name for b in self.bars]

    @property
    def end_balance_in(self):
        return [b.end_balance_in for b in self.bars]

    @property
    def end_balance_out(self):
        return [b.end_balance_out for b in self.bars]

class WaterbalanceItemTable(QTableView):
    hoverExitRow = pyqtSignal(int)
    hoverExitAllRows = pyqtSignal()  # exit the whole widget
    hoverEnterRow = pyqtSignal(str)

    def __init__(self, parent=None):
        super(WaterbalanceItemTable, self).__init__(parent)
        self.setStyleSheet("QTreeView::item:hover{background-color:#FFFF00;}")
        self.setMouseTracking(True)
        self.model = None

        self._last_hovered_row = None
        self.viewport().installEventFilter(self)

    def on_close(self):
        """
        unloading widget and remove all required stuff
        :return:
        """
        self.setMouseTracking(False)
        self.viewport().removeEventFilter(self)

    def closeEvent(self, event):
        """
        overwrite of QDockWidget class to emit signal
        :param event: QEvent
        """
        self.on_close()
        event.accept()

    def eventFilter(self, widget, event):
        if widget is self.viewport():

            if event.type() == QEvent.MouseMove:
                row = self.indexAt(event.pos()).row()
                if row == 0 and self.model and row > self.model.rowCount():
                    row = None

            elif event.type() == QEvent.Leave:
                row = None
                self.hoverExitAllRows.emit()
            else:
                row = self._last_hovered_row

            if row != self._last_hovered_row:
                if self._last_hovered_row is not None:
                    try:
                        self.hover_exit(self._last_hovered_row)
                    except IndexError:
                        log.warning(
                            "Hover row index %s out of range",
                            self._last_hovered_row)
                        # self.hoverExitRow.emit(self._last_hovered_row)
                # self.hoverEnterRow.emit(row)
                if row is not None:
                    try:
                        self.hover_enter(row)
                    except IndexError:
                        log.warning("Hover row index %s out of range", row),
                self._last_hovered_row = row
                pass
        return QTableView.eventFilter(self, widget, event)

    def hover_exit(self, row_nr):
        if row_nr >= 0:
            item = self.model.rows[row_nr]
            item.color.value = item.color.value[:3] + [150]
            item.hover.value = False

    def hover_enter(self, row_nr):
        if row_nr >= 0:
            item = self.model.rows[row_nr]
            name = item.name.value
            self.hoverEnterRow.emit(name)
            item.color.value = item.color.value[:3] + [220]
            item.hover.value = True

    def setModel(self, model):
        super(WaterbalanceItemTable, self).setModel(model)

        self.model = model

        self.resizeColumnsToContents()
        self.model.set_column_sizes_on_view(self)

class WaterBalancePlotWidget(pg.PlotWidget):
    def __init__(self, parent=None, name=""):

        super(WaterBalancePlotWidget, self).__init__(parent)
        self.name = name
        self.showGrid(True, True, 0.5)
        self.setLabel("bottom", "tijd", "s")
        self.setLabel("left", "Debiet", "m3/s")
        # Auto SI prefix scaling doesn't work properly with m3, m2 etc.
        self.getAxis("left").enableAutoSIPrefix(False)
        self.series = {}

    def setModel(self, model):
        self.model = model
        self.model.dataChanged.connect(self.data_changed)
        self.model.rowsInserted.connect(self.on_insert)
        self.model.rowsAboutToBeRemoved.connect(
            self.on_remove)

    def on_remove(self):
        self.draw_timeseries()

    def on_insert(self):
        self.draw_timeseries()

    def draw_timeseries(self):
        self.clear()

        ts = self.model.ts

        zeros = np.zeros(shape=(np.size(ts, 0),))
        zero_serie = pg.PlotDataItem(
            x=ts,
            y=zeros,
            connect='finite',
            pen=pg.mkPen(color=QColor(0, 0, 0, 220), width=1))
        self.addItem(zero_serie)

        for dir in ['in', 'out']:
            prev_serie = zeros
            prev_pldi = zero_serie
            for item in self.model.rows:
                if item.active.value:
                    cum_serie = prev_serie + item.ts_series.value[dir]
                    plot_item = pg.PlotDataItem(
                        x=ts,
                        y=cum_serie,
                        connect='finite',
                        pen=pg.mkPen(color=QColor(*item.color.value), width=1))

                    color = item.color.value
                    fill = pg.FillBetweenItem(prev_pldi,
                                              plot_item,
                                              pg.mkBrush(*color))

                    # keep reference
                    item._plots[dir] = plot_item
                    item._plots[dir + 'fill'] = fill
                    prev_serie = cum_serie
                    prev_pldi = plot_item

        for dir in ['in', 'out']:
            for item in reversed(self.model.rows):
                if item.active.value:
                    self.addItem(item._plots[dir])
                    self.addItem(item._plots[dir + 'fill'])

        self.autoRange()

    def data_changed(self, index):
        """
        change graphs based on changes in locations
        :param index: index of changed field
        """
        if self.model.columns[index.column()].name == 'active':
            self.draw_timeseries()

        elif self.model.columns[index.column()].name == 'hover':
            item = self.model.rows[index.row()]
            if item.hover.value:
                if item.active.value:
                    if 'in' in item._plots:
                        item._plots['in'].setPen(color=item.color.value,
                                                 width=1)
                        item._plots['infill'].setBrush(
                            pg.mkBrush(item.color.value))
                    if 'out' in item._plots:
                        item._plots['out'].setPen(color=item.color.value,
                                                  width=1)
                        item._plots['outfill'].setBrush(
                            pg.mkBrush(item.color.value))
            else:
                if item.active.value:
                    if 'in' in item._plots:
                        item._plots['in'].setPen(color=item.color.value,
                                                 width=1)
                        item._plots['infill'].setBrush(
                            pg.mkBrush(item.color.value))
                    if 'out' in item._plots:
                        item._plots['out'].setPen(color=item.color.value,
                                                 width=1)
                    item._plots['outfill'].setBrush(
                        pg.mkBrush(item.color.value))

class WaterBalanceWidget(QDockWidget):
    closingWidget = pyqtSignal()

    INPUT_SERIES = INPUT_SERIES

    IN_OUT_SERIES = [
        {
            'label_name': '1D-2D flow',
            'in': ['1d_2d_in'],
            'out': ['1d_2d_in'],
            'type': '1d',
        }, {
            'label_name': '1D-2D flow',
            'in': ['1d_2d_out'],
            'out': ['1d_2d_out'],
            'type': '2d',
        }, {
            'label_name': '1D-2D flow (all domains)',
            # does this make sense?
            'in': ['1d_2d_in', '1d_2d_out'],
            'out': ['1d_2d_in', '1d_2d_out'],
            'type': 'NETVOL',
        }, {
            'label_name': 'net change in storage',
            'in': ['d_2d_vol'],
            'out': ['d_2d_vol'],
            'type': '2d',
        }, {
            'label_name': 'net change in storage',
            'in': ['d_1d_vol'],
            'out': ['d_1d_vol'],
            'type': '1d',
        }, {
            'label_name': 'net change in storage',
            'in': ['d_2d_groundwater_vol'],
            'out': ['d_2d_groundwater_vol'],
            'type': '2d_groundwater',
        }, {
            'label_name': 'leakage',
            'in': ['leak'],
            'out': ['leak'],
            'type': '2d_groundwater',
        }, {
            'label_name': 'simple infiltration',
            'in': ['infiltration_rate_simple'],
            'out': ['infiltration_rate_simple'],
            'type': '2d',
        }, {
            'label_name': '2D flow',
            'in': ['2d_in'],
            'out': ['2d_out'],
            'type': '2d',
        }, {
            'label_name': '1D flow',
            'in': ['1d_in'],
            'out': ['1d_out'],
            'type': '1d',
        }, {
            'label_name': '2D groundwater flow',
            'in': ['2d_groundwater_in'],
            'out': ['2d_groundwater_out'],
            'type': '2d_groundwater',
        }, {
            'label_name': '2D laterals',
            'in': ['lat_2d'],
            'out': ['lat_2d'],
            'type': '2d',
        }, {
            'label_name': '1D laterals',
            'in': ['lat_1d'],
            'out': ['lat_1d'],
            'type': '1d',
        }, {
            'label_name': '2D boundaries',
            'in': ['2d_bound_in'],
            'out': ['2d_bound_out'],
            'type': '2d',
        }, {
            'label_name': '1D boundaries',
            'in': ['1d_bound_in'],
            'out': ['1d_bound_out'],
            'type': '1d',
        }, {
            'label_name': '1D-2D exchange',
            'in': ['2d_to_1d_pos'],
            'out': ['2d_to_1d_neg'],
            'type': '1d_2d',
        }, {
            'label_name': '1D inflow from rain',
            'in': ['inflow'],
            'out': ['inflow'],
            'type': '1d',
        }, {
            'label_name': 'infiltration/exfiltration (domain exchange)',
            # NOTE: for the argument why pos is out and neg is in, see the
            # comment in ``WaterBalanceCalculation.get_aggregated_flows``
            'in': ['2d_vertical_infiltration_neg'],
            'out': ['2d_vertical_infiltration_pos'],
            'type': '2d_vert',
        }, {
            'label_name': 'change in storage',
            'in': ['d_2d_vol', 'd_2d_groundwater_vol', 'd_1d_vol'],
            'out': ['d_2d_vol', 'd_2d_groundwater_vol', 'd_1d_vol'],
            'type': 'NETVOL',
        }, {
            'label_name': 'pump',
            'in': ['pump_in'],
            'out': ['pump_out'],
            'type': '1d',
        }, {
            'label_name': 'rain',
            'in': ['rain'],
            'out': ['rain'],
            'type': '2d',
        }
    ]

    def __init__(
            self, parent=None, iface=None, ts_datasource=None, wb_calc=None):
        """Constructor."""
        super(WaterBalanceWidget, self).__init__(parent)

        self.aggregation_warning_issued_on_start = False
        self.iface = iface
        self.ts_datasource = ts_datasource
        self.calc = wb_calc

        # setup ui
        self.setup_ui(self)

        self.model = WaterbalanceItemModel()
        self.wb_item_table.setModel(self.model)
        self.plot_widget.setModel(self.model)

        # link tool
        self.polygon_tool = PolygonDrawTool(self.iface.mapCanvas(),
                                            self.select_polygon_button,
                                            self.on_polygon_ready)

        # fill comboboxes with selections
        self.modelpart_combo_box.insertItems(
            0,
            ['1d 2d', '1d', '2d'])
        #  self.modelpart_combo_box.setCurrentIndex(0)
        self.source_nc_combo_box.insertItems(
            0,
            ['normal', 'aggregation'])
        self.sum_type_combo_box.insertItems(
            0,
            serie_settings.keys())

        self.agg_combo_box.insertItems(
            0,
            ['m3/s natural', 'm3 cumulative natural', 'm3/s',
             'm3 cumulative'])

        # add listeners
        self.select_polygon_button.toggled.connect(self.toggle_polygon_button)
        self.reset_waterbalans_button.clicked.connect(self.reset_waterbalans)
        self.chart_button.clicked.connect(self.show_chart)
        # self.polygon_tool.deactivated.connect(self.update_wb)
        self.modelpart_combo_box.currentIndexChanged.connect(self.update_wb)
        self.source_nc_combo_box.currentIndexChanged.connect(self.update_wb)
        self.source_nc_combo_box.currentIndexChanged.connect(
            self.issue_warning)
        self.sum_type_combo_box.currentIndexChanged.connect(self.update_wb)
        self.agg_combo_box.currentIndexChanged.connect(self.update_wb)
        self.wb_item_table.hoverEnterRow.connect(
            self.hover_enter_map_visualization)
        self.wb_item_table.hoverExitAllRows.connect(
            self.hover_exit_map_visualization)

        # TODO: is this a good default?
        # initially turn on tool
        self.select_polygon_button.toggle()

        self.__current_calc = None  # cache the results of calculation

    def show_chart(self):
        if not self._current_calc:
            return
        ts, ts_series = self._current_calc

        io_series_net = [
            x for x in self.IN_OUT_SERIES if (
                x['type'] in [
                    '2d', '1d_2d', '2d_vert', '2d_groundwater', '1d'] and
                'storage' not in x['label_name'] and
                'exchange' not in x['label_name'] and
                x['label_name'] != '1D-2D flow') or
            x['type'] == 'NETVOL'
        ]

        io_series_2d = [
            x for x in self.IN_OUT_SERIES if x['type'] in
            ['2d', '1d_2d', '2d_vert']
        ]

        io_series_2d_groundwater = [
            x for x in self.IN_OUT_SERIES if x['type'] in [
                '2d_groundwater', '2d_vert']
        ]

        io_series_1d = [
            x for x in self.IN_OUT_SERIES if x['type'] in [
                '1d', '1d_2d']
        ]

        # get timeseries x range in plot widget
        viewbox_state = self.plot_widget.getPlotItem().getViewBox().getState()
        view_range = viewbox_state['viewRange']
        t1, t2 = view_range[0]

        bm_net = BarManager(io_series_net)
        bm_2d = BarManager(io_series_2d)
        bm_2d_groundwater = BarManager(io_series_2d_groundwater)
        bm_1d = BarManager(io_series_1d)

        bm_net.calc_balance(ts, ts_series, t1, t2, net=True)
        bm_2d.calc_balance(ts, ts_series, t1, t2)
        bm_2d_groundwater.calc_balance(
            ts, ts_series, t1, t2,
            invert=['infiltration/exfiltration (domain exchange)']
        )
        bm_1d.calc_balance(ts, ts_series, t1, t2, invert=['1D-2D exchange'])

        # init figure
        plt.close()
        plt.figure(1)  # TODO: what does this do?
        plt.suptitle("Water balance from t=%.2f to t=%.2f" % (t1, t2))

        # prevent clipping of tick-labels, among others
        plt.subplots_adjust(
            bottom=.3, top=.9, left=.125, right=.9, hspace=1, wspace=.4)

        pattern = '//'

        # #####
        # Net #
        # #####

        plt.subplot(221)
        plt.axhline(color='black', lw=.5)
        bar_in = plt.bar(bm_net.x, bm_net.end_balance_in, label='In')
        bar_out = plt.bar(bm_net.x, bm_net.end_balance_out, label='Out')
        bar_in[-1].set_hatch(pattern)
        bar_out[-1].set_hatch(pattern)
        plt.xticks(bm_net.x, bm_net.xlabels, rotation=45, ha='right')
        plt.title('Net water balance')
        plt.ylabel(r'volume ($m^3$)')
        plt.legend()

        # ####
        # 2D #
        # ####

        # this axes object will be shared by the other subplots to give them
        # the same y alignment
        ax1 = plt.subplot(234)

        plt.axhline(color='black', lw=.5)
        bar_in = plt.bar(bm_2d.x, bm_2d.end_balance_in, label='In')
        bar_out = plt.bar(bm_2d.x, bm_2d.end_balance_out, label='Out')
        bar_in[-1].set_hatch(pattern)
        bar_out[-1].set_hatch(pattern)
        plt.xticks(bm_2d.x, bm_2d.xlabels, rotation=45, ha='right')
        plt.title('2D surface water domain')
        plt.ylabel(r'volume ($m^3$)')
        plt.legend()

        # ################
        # 2D groundwater #
        # ################

        plt.subplot(235, sharey=ax1)
        plt.axhline(color='black', lw=.5)
        bar_in = plt.bar(
            bm_2d_groundwater.x, bm_2d_groundwater.end_balance_in, label='In')
        bar_out = plt.bar(
            bm_2d_groundwater.x, bm_2d_groundwater.end_balance_out,
            label='Out')
        bar_in[-1].set_hatch(pattern)
        bar_out[-1].set_hatch(pattern)
        plt.xticks(
            bm_2d_groundwater.x, bm_2d_groundwater.xlabels, rotation=45,
            ha='right')
        plt.title('2D groundwater domain')
        plt.ylabel(r'volume ($m^3$)')
        plt.legend()

        # ####
        # 1D #
        # ####

        plt.subplot(236, sharey=ax1)
        plt.axhline(color='black', lw=.5)
        bar_in = plt.bar(bm_1d.x, bm_1d.end_balance_in, label='In')
        bar_out = plt.bar(bm_1d.x, bm_1d.end_balance_out, label='Out')
        bar_in[-1].set_hatch(pattern)
        bar_out[-1].set_hatch(pattern)
        plt.xticks(bm_1d.x, bm_1d.xlabels, rotation=45, ha='right')
        plt.title('1D network domain')
        plt.ylabel(r'volume ($m^3$)')
        plt.legend()

        plt.show()

    def hover_enter_map_visualization(self, name):
        """On hover rubberband visualisation using the table item name.

        Uses the cached self.qgs_lines/self.qgs_points.
        """
        if self.select_polygon_button.isChecked():
            # highlighting when drawing the polygon doesn't look right.
            # this is the best solution I can think of atm...
            return
        types_2d_line = [
            '2d flow',
        ]
        types_2d_node = [
            'volumeverandering 2d',
        ]
        # TODO 1: generate this dict

        # TODO 2: 1d-2d flow isn't completely correct for 2d
        # and 1d flow because those only only take either 1d_2d_in or
        # 1d_2d_out and we're using the aggregated 1d_2d_intersected

        # TODO 3: using the name as key is INCREDIBLY error prone: one
        # spelling mistake or a change in sum_configs and it doesn't work
        # anymore, and because we also catch the KeyErrors you won't even
        # notice. NEEDS TO BE FIXED

        NAME_TO_LINE_TYPES_ALLES = {
            '2d flow': ['2d'],
            '2d boundaries': ['2d_bound'],
            '1d flow': ['1d'],
            '1d boundaries': ['1d_bound'],
            '1d-2d uitwisseling': ['1d_2d'],
            # TODO: '1d_2d_intersected' and 'pump_or_whatever' are magic
            # strings that we ad-hoc created in the
            # prepare_and_visualize_selection function.
            # A better solution would be nice...
            '1d-2d flow': ['1d_2d_intersected'],
            'pompen': ['pump_or_whatever'],
            '2d groundwater flow': ['2d_groundwater'],
        }
        NAME_TO_LINE_TYPES_HOOFDSTROMEN = {
            '2d flow': ['2d', '2d_bound', '1d_2d_intersected'],
            '1d flow': [
                '1d', 'pump_or_whatever', '1d_bound', '1d_2d_intersected'],
            '1d-2d uitwisseling': ['1d_2d'],
            '2d groundwater flow': ['2d_groundwater'],
        }
        NAME_TO_NODE_TYPES = {
            'volumeverandering': ['1d', '2d', '2d_groundwater'],
            'volumeverandering 2d': ['2d'],
            'volumeverandering 1d': ['1d'],
            'volumeverandering 2d grondwater': ['2d_groundwater'],
            'neerslag': ['2d'],
            'inflow 1d from rain': ['1d'],
            'lateraal 1d': ['1d'],
            'lateraal 2d': ['2d'],
            'leakage': ['2d'],
            'infiltratie': ['2d'],
            'belasting (regen en lateralen)': ['1d', '2d'],
        }

        # more hackery to fix keys defined in both 'hoofdstromen'
        # and 'alles'.
        sum_type = self.sum_type_combo_box.currentText()
        assert sum_type in ['hoofdstromen', 'alles']
        if sum_type == 'hoofdstromen':
            name_to_line_type = NAME_TO_LINE_TYPES_HOOFDSTROMEN
        elif sum_type == 'alles':
            name_to_line_type = NAME_TO_LINE_TYPES_ALLES
        else:
            raise ValueError("Unknown type %s" % sum_type)

        try:
            types_line = name_to_line_type[name]
        except KeyError:
            line_geoms = []
        else:
            line_geoms = []
            for t in types_line:
                try:
                    geoms = self.qgs_lines[t]
                except KeyError:
                    continue
                line_geoms.extend(geoms)
        try:
            types_node = NAME_TO_NODE_TYPES[name]
        except KeyError:
            point_geoms = []
        else:
            point_geoms = []
            for t in types_node:
                try:
                    geoms = self.qgs_points[t]
                except KeyError:
                    continue
                point_geoms.extend(geoms)
        self.polygon_tool.selection_vis.update(line_geoms, point_geoms)

    def hover_exit_map_visualization(self, *args):
        self.polygon_tool.selection_vis.reset()

    def on_polygon_ready(self, points):
        self.iface.mapCanvas().unsetMapTool(self.polygon_tool)

    def reset_waterbalans(self):
        self.polygon_tool.reset()

    def toggle_polygon_button(self):

        if self.select_polygon_button.isChecked():
            self.reset_waterbalans()

            self.iface.mapCanvas().setMapTool(self.polygon_tool)

            self.select_polygon_button.setText(_translate(
                "DockWidget", "Gebied tekenen afronden", None))
        else:
            self.iface.mapCanvas().unsetMapTool(self.polygon_tool)
            self.update_wb()
            self.select_polygon_button.setText(_translate(
                "DockWidget", "Teken nieuw gebied", None))

            if not self.aggregation_warning_issued_on_start:
                self.issue_warning()
                self.aggregation_warning_issued_on_start = True

    def redraw_wb(self):
        pass

    def update_wb(self):

        try:
            ts, graph_series = self.calc_wb(
                self.modelpart_combo_box.currentText(),
                self.source_nc_combo_box.currentText(),
                self.agg_combo_box.currentText(),
                serie_settings[self.sum_type_combo_box.currentText()])
        except self.calc.AggregationFileNotFoundError:
            QMessageBox.warning(
                None,
                "No aggregation file found",
                "The 'aggregation' option requires an aggregation NetCDF "
                "file with the following variables:"
                "\n\ncumulative:\n- rain\n- infiltration\n- laterals"
                "\n- leakage\n- discharge\n- pump discharge"
                "\n\npositive cumulative:\n- discharge"
                "\n\nnegative cumulative:\n- discharge"
            )
            return

        self.model.removeRows(0, len(self.model.rows))

        self.model.ts = ts
        self.model.insertRows(graph_series['items'])

        if self.agg_combo_box.currentText() == 'm3/s':
            self.plot_widget.setLabel("left", "Debiet", "m3/s")
        elif self.agg_combo_box.currentText() == 'm3 cumulative':
            self.plot_widget.setLabel("left", "Cumulatieve debiet", "m3")
        elif self.agg_combo_box.currentText() == 'm3/s natural':
            self.plot_widget.setLabel("left", "Debiet", "m3/s")
        elif self.agg_combo_box.currentText() == 'm3 cumulative natural':
            self.plot_widget.setLabel("left", "Cumulatieve debiet", "m3")
        else:
            self.plot_widget.setLabel("left", "-", "-")

        # set labels for in and out fluxes
        text_upper = pg.TextItem(text="in", anchor=(0, 1), angle=-90)
        text_upper.setPos(0, 0)
        text_lower = pg.TextItem(text="uit", anchor=(1, 1), angle=-90)
        text_lower.setPos(0, 0)
        self.plot_widget.addItem(text_upper)
        self.plot_widget.addItem(text_lower)

    @property
    def _current_calc(self):
        return self.__current_calc

    @_current_calc.setter
    def _current_calc(self, ts_total_time_tuple):
        # NOTE: flips the sign on dvol to make things more intuitive for
        # barcharts
        ts, ts_series = ts_total_time_tuple
        ts_series = ts_series.copy()
        # indices for dvol of 2d, 1d, and groundwater
        if self.reverse_dvol_sign:
            ts_series[:, [18, 19, 25]] = -1 * ts_series[:, [18, 19, 25]]
        self.__current_calc = (ts, ts_series)

    @property
    def reverse_dvol_sign(self):
        aggregation_type = self.agg_combo_box.currentText()
        return aggregation_type in ['m3/s', 'm3 cumulative']

    def calc_wb(self, model_part, source_nc, aggregation_type, settings):
        poly_points = self.polygon_tool.points
        wb_polygon = QgsGeometry.fromPolygon([poly_points])

        lines, points, pumps = \
            self.ts_datasource.rows[0].get_result_layers()
        tr = QgsCoordinateTransform(
            self.iface.mapCanvas().mapRenderer().destinationCrs(), lines.crs())
        wb_polygon.transform(tr)

        link_ids, pump_ids = self.calc.get_incoming_and_outcoming_link_ids(
            wb_polygon, model_part)
        node_ids = self.calc.get_nodes(wb_polygon, model_part)

        ts, total_time = self.calc.get_aggregated_flows(
            link_ids, pump_ids, node_ids, model_part, source_nc,
            reverse_dvol_sign=self.reverse_dvol_sign)

        # cache data for barchart
        self._current_calc = (ts, total_time)

        graph_series = self.make_graph_series(
            ts, total_time, model_part, aggregation_type, settings)

        self.prepare_and_visualize_selection(
            link_ids, pump_ids, node_ids, lines, pumps, points)

        return ts, graph_series

    def prepare_and_visualize_selection(
            self, link_ids, pump_ids, node_ids, lines, pumps, points,
            draw_it=False):
        """Prepare dictionaries with geometries categorized by type and
        save it on self.qgs_lines and self.qgs_points.
        """
        req_filter_links = _get_request_filter(link_ids)
        req_filter_pumps = _get_request_filter(pump_ids)
        req_filter_nodes = _get_request_filter(node_ids)

        line_id_to_type = {}
        for _type, id_list in link_ids.items():
            for i in id_list:
                # we're not interested in in or out types, but for 1d_2d_in
                # and 1d_2d_out we need to employ this hack because these
                # types end in '_in'/'_out'
                if _type == '1d_2d_in' or _type == '1d_2d_out':
                    t = '1d_2d_intersected'
                else:
                    t = _type.rsplit('_out')[0].rsplit('_in')[0]

                if i not in line_id_to_type:
                    # business as usual
                    line_id_to_type[i] = t
                else:
                    # NOTE: awful hack for links that have multiple types
                    val = line_id_to_type[i]
                    if isinstance(val, list):
                        val.append(t)
                    else:
                        line_id_to_type[i] = [val, t]

        # pump_id_to_type = {}
        # for _, id_list in pump_ids.items():
        #     for i in id_list:
        #         pump_id_to_type[i] = 'all'
        node_id_to_type = {}
        for _type, id_list in node_ids.items():
            for i in id_list:
                node_id_to_type[i] = _type

        qgs_lines = {}
        qgs_points = {}
        tr_reverse = QgsCoordinateTransform(
            lines.crs(),
            self.iface.mapCanvas().mapRenderer().destinationCrs(),
        )

        # NOTE: getting all features again isn't efficient because they're
        # already calculated in WaterBalanceCalculation, but w/e
        for feat in _get_feature_iterator(lines, req_filter_links):
            geom = feat.geometry()
            geom.transform(tr_reverse)
            _type = line_id_to_type[feat['id']]

            if isinstance(_type, list):
                # NOTE: this means there are multiple types for one link
                for t in _type:
                    qgs_lines.setdefault(t, []).append(geom.asPolyline())
            else:
                # one type only, business as usual
                qgs_lines.setdefault(_type, []).append(geom.asPolyline())
        for feat in _get_feature_iterator(pumps, req_filter_pumps):
            geom = feat.geometry()
            geom.transform(tr_reverse)
            qgs_lines.setdefault('pump_or_whatever', []).append(
                geom.asPolyline())
        for feat in _get_feature_iterator(points, req_filter_nodes):
            geom = feat.geometry()
            geom.transform(tr_reverse)
            _type = node_id_to_type[feat['id']]
            qgs_points.setdefault(_type, []).append(geom.asPoint())

        self.qgs_lines = qgs_lines
        self.qgs_points = qgs_points

        # draw the lines/points immediately
        # TODO: probably need to throw this code away since we won't use it
        if draw_it:
            qgs_lines_all = [j for i in qgs_lines.values() for j in i]
            qgs_points_all = [j for i in qgs_points.values() for j in i]

            self.polygon_tool.update_line_point_selection(
                qgs_lines_all, qgs_points_all)

    def make_graph_series(
            self, ts, total_time, model_part, aggregation_type, settings):
        settings = copy.deepcopy(settings)

        if model_part == '1d 2d':
            input_series = dict([
                (x, y) for (x, y, z) in self.INPUT_SERIES
                if z in ['2d', '1d', 'error_1d_2d']])
        elif model_part == '2d':
            input_series = dict([
                (x, y) for (x, y, z) in self.INPUT_SERIES
                if z in ['2d', '1d_2d', 'error_2d']])
        elif model_part == '1d':
            input_series = dict([
                (x, y) for (x, y, z) in self.INPUT_SERIES
                if z in ['1d', '1d_2d', 'error_1d']])
            idx_2d_to_1d_pos = input_series['2d_to_1d_pos']
            idx_2d_to_1d_neg = input_series['2d_to_1d_neg']
            idx_2d_to_1d = (idx_2d_to_1d_pos, idx_2d_to_1d_neg)
            total_time[:, idx_2d_to_1d] = total_time[:, idx_2d_to_1d] * -1

        # TODO: figure out why the hell np.clip is needed.
        for serie_setting in settings.get('items', []):
            serie_setting['active'] = True
            serie_setting['method'] = serie_setting['default_method']
            serie_setting['color'] = [int(c) for c in serie_setting['def_color'].split(',')] + [150]
            serie_setting['ts_series'] = {}
            nrs_input_series = []
            for serie in serie_setting['series']:
                if serie not in input_series:
                    # throw good error message
                    log.warning('serie config error: %s is an unknown serie or is doubled in the config.', serie)
                else:
                    nrs_input_series.append(input_series[serie])
                    del input_series[serie]

            if serie_setting['default_method'] == 'net':
                sum = total_time[:, nrs_input_series].sum(axis=1)
                serie_setting['ts_series']['in'] = sum.clip(min=0)
                serie_setting['ts_series']['out'] = sum.clip(max=0)
            elif serie_setting['default_method'] == 'gross':
                sum_pos = np.zeros(shape=(np.size(ts, 0),))
                sum_neg = np.zeros(shape=(np.size(ts, 0),))
                for nr in nrs_input_series:
                    sum_pos += total_time[:, nr].clip(min=0)
                    sum_neg += total_time[:, nr].clip(max=0)
                serie_setting['ts_series']['in'] = sum_pos
                serie_setting['ts_series']['out'] = sum_neg
            else:
                # throw config error
                log.warning('aggregation %s method unknown.', serie_setting['default_method'])

            if aggregation_type == 'm3 cumulative' or \
                    aggregation_type == 'm3 cumulative natural':
                log.debug('aggregate')
                diff = np.append([0], np.diff(ts))

                serie_setting['ts_series']['in'] = serie_setting['ts_series']['in'] * diff
                serie_setting['ts_series']['in'] = np.cumsum(serie_setting['ts_series']['in'], axis=0)

                serie_setting['ts_series']['out'] = serie_setting['ts_series']['out'] * diff
                serie_setting['ts_series']['out'] = np.cumsum(serie_setting['ts_series']['out'], axis=0)

        if len(input_series) > 0:

            serie_setting = {
                'name': 'Overige',
                'default_method': settings['remnant_method'],
                'order': 100,
                'color': [int(c) for c in settings['remnant_def_color'].split(',')] + [150],
                'def_color': settings['remnant_def_color'], #TODO: fix + [150],
                'series': [key for key in input_series],
                'ts_series': {}
            }
            for serie in input_series:
                nrs_input_series.append(input_series[serie])

            if serie_setting['default_method'] == 'net':
                sum = total_time[:, nrs_input_series].sum(axis=1)
                serie_setting['ts_series']['in'] = sum.clip(min=0)
                serie_setting['ts_series']['out'] = sum.clip(max=0)
            elif serie_setting['default_method'] == 'gross':
                sum_pos = np.zeros(shape=(np.size(ts, 0),))
                sum_neg = np.zeros(shape=(np.size(ts, 0),))
                for nr in nrs_input_series:
                    sum_pos += total_time[:, nr].clip(min=0)
                    sum_neg += total_time[:, nr].clip(max=0)
                serie_setting['ts_series']['in'] = sum_pos
                serie_setting['ts_series']['out'] = sum_neg
            else:
                # throw config error
                log.warning('aggregation %s method unknown.', serie_setting['default_method'])

            settings['items'].append(serie_setting)

        if model_part == '1d':
            total_time[:, (10, 11)] = total_time[:, (10, 11)] * -1

        settings['items'] = sorted(settings['items'], key=lambda item: item['order'])

        return settings

    def unset_tool(self):
        pass

    def accept(self):
        pass

    def reject(self):
        self.close()

    def closeEvent(self, event):
        self.select_polygon_button.toggled.disconnect(
            self.toggle_polygon_button)
        self.reset_waterbalans_button.clicked.disconnect(
            self.reset_waterbalans)
        self.chart_button.clicked.disconnect(self.show_chart)
        # self.polygon_tool.deactivated.disconnect(self.update_wb)
        self.iface.mapCanvas().unsetMapTool(self.polygon_tool)
        self.polygon_tool.close()

        self.modelpart_combo_box.currentIndexChanged.disconnect(self.update_wb)
        self.source_nc_combo_box.currentIndexChanged.disconnect(self.update_wb)
        self.sum_type_combo_box.currentIndexChanged.disconnect(self.update_wb)
        self.wb_item_table.hoverEnterRow.disconnect(
            self.hover_enter_map_visualization)
        self.wb_item_table.hoverExitAllRows.disconnect(
            self.hover_exit_map_visualization)

        self.closingWidget.emit()
        event.accept()

    def issue_warning(self):
        mode = self.source_nc_combo_box.currentText()
        if mode == 'normal':
            QMessageBox.information(
                None,
                "Information",
                "You're currently using the 'normal' NetCDF result file which "
                "may result in a less accurate overall water balance "
                "compared to the 'aggregation' NetCDF file, depending on the "
                "output time step that was chosen. Proceed at your own "
                "discretion. Please select the 'aggregation' option "
                "for the most accurate results."
            )

    def setup_ui(self, dock_widget):
        """
        initiate main Qt building blocks of interface
        :param dock_widget: QDockWidget instance
        """

        dock_widget.setObjectName("dock_widget")
        dock_widget.setAttribute(Qt.WA_DeleteOnClose)

        self.dock_widget_content = QWidget(self)
        self.dock_widget_content.setObjectName("dockWidgetContent")

        self.main_vlayout = QVBoxLayout(self)
        self.dock_widget_content.setLayout(self.main_vlayout)

        # add button to add objects to graphs
        self.button_bar_hlayout = QHBoxLayout(self)
        self.select_polygon_button = QPushButton(self)
        self.select_polygon_button.setCheckable(True)
        self.select_polygon_button.setObjectName("SelectedSideview")
        self.button_bar_hlayout.addWidget(self.select_polygon_button)
        self.reset_waterbalans_button = QPushButton(self)
        self.reset_waterbalans_button.setObjectName("ResetSideview")
        self.button_bar_hlayout.addWidget(self.reset_waterbalans_button)
        self.chart_button = QPushButton(self)
        self.button_bar_hlayout.addWidget(self.chart_button)

        self.modelpart_combo_box = QComboBox(self)
        self.button_bar_hlayout.addWidget(self.modelpart_combo_box)
        self.source_nc_combo_box = QComboBox(self)
        self.button_bar_hlayout.addWidget(self.source_nc_combo_box)
        self.sum_type_combo_box = QComboBox(self)
        self.button_bar_hlayout.addWidget(self.sum_type_combo_box)

        self.agg_combo_box = QComboBox(self)
        self.button_bar_hlayout.addWidget(self.agg_combo_box)

        spacer_item = QSpacerItem(40,
                                  20,
                                  QSizePolicy.Expanding,
                                  QSizePolicy.Minimum)
        self.button_bar_hlayout.addItem(spacer_item)
        self.main_vlayout.addLayout(self.button_bar_hlayout)

        # add tabWidget for graphWidgets
        self.contentLayout = QHBoxLayout(self)

        # Graph
        self.plot_widget = WaterBalancePlotWidget(self)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(1)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(
            self.plot_widget.sizePolicy().hasHeightForWidth())
        self.plot_widget.setSizePolicy(sizePolicy)
        self.plot_widget.setMinimumSize(QSize(250, 250))

        self.contentLayout.addWidget(self.plot_widget)

        # table
        self.wb_item_table = WaterbalanceItemTable(self)
        sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.wb_item_table.sizePolicy().hasHeightForWidth())
        self.wb_item_table.setSizePolicy(sizePolicy)
        self.wb_item_table.setMinimumSize(QSize(300, 0))

        self.contentLayout.addWidget(self.wb_item_table)

        self.main_vlayout.addLayout(self.contentLayout)

        # add dockwidget
        dock_widget.setWidget(self.dock_widget_content)
        self.retranslate_ui(dock_widget)
        QMetaObject.connectSlotsByName(dock_widget)

    def retranslate_ui(self, dock_widget):
        pass
        dock_widget.setWindowTitle(_translate(
            "DockWidget", "3Di waterbalans", None))
        self.select_polygon_button.setText(_translate(
            "DockWidget", "Teken nieuw gebied", None))
        self.chart_button.setText(_translate(
            "DockWidget", "Toon totale balans", None))
        self.reset_waterbalans_button.setText(_translate(
            "DockWidget", "Verberg op kaart", None))


#!/bin/python
import sys
filen = sys.argv[1]
path = sys.argv[2]

if( 'json' in filen):
	with open(filen, "r") as in_file:
		buf = in_file.readlines()

	with open(filen, "w") as out_file:
		for line in buf:
			if line == ' "argv": [\n':
				line = line + '"'+path+'",\n'
			out_file.write(line)
elif( 'kernel_cosmosis' in filen):
	with open(filen, "r") as in_file:
		buf = in_file.readlines()

	with open(filen, "w") as out_file:
		for line in buf:
			if line == 'source activate cosmosis-nb-y1\n':
				line = line + 'export COSMOSIS_SRC_DIR="'+path+'"\n'
			out_file.write(line)


import cherrypy
import datetime
from urllib.parse import urlparse

from girder.exceptions import RestException, ValidationException
from girder.api import access
from girder.api.describe import Description, autoDescribeRoute
from girder.api.rest import Resource, getApiUrl
from girder.models.setting import Setting
from girder.models.token import Token
from girder.models.user import User
from girder.plugins.oauth import constants as OAuthConstants
from girder.plugins.oauth import providers

from ..constants import PluginSettings
from ..lib import Verificators

class Account(Resource):
    def __init__(self):
        super(Account, self).__init__()
        self.resourceName = "account"

        self.route("GET", (), self.listAccounts)
        self.route("GET", (":provider", "revoke"), self.revokeAccount)
        self.route("GET", (":provider", "callback"), self.callbackAccount)
        self.route("GET", (":provider", "targets"), self.listTargetsForAccount)
        self.route("POST", (":provider", "key"), self.addAccountKey)

    @staticmethod
    def supported_providers():
        """Converts list of providers into a providerName -> provider."""
        return {
            _["name"]: _ for _ in Setting().get(PluginSettings.EXTERNAL_AUTH_PROVIDERS)
        }

    @staticmethod
    def supported_apikey_flavors():
        """Converts list of apikey targets into a provider -> possible targets."""
        return {
            _["name"]: _["targets"]
            for _ in Setting().get(PluginSettings.EXTERNAL_APIKEY_GROUPS)
        }

    @staticmethod
    def _createStateToken(redirect, user=None):
        """
        Creates CSRF token that is user specific.

        This allows us to recover user, once the callback is called.
        """
        csrfToken = Token().createToken(user=user, days=0.25)

        # The delimiter is arbitrary, but a dot doesn't need to be URL-encoded
        state = "%s.%s" % (csrfToken["_id"], redirect)
        return state

    @staticmethod
    def _validateCsrfToken(state):
        """
        Tests the CSRF token value in the cookie to authenticate the user as
        the originator of the account integration. Raises a RestException if the token
        is invalid.
        """
        csrfTokenId, _, redirect = state.partition(".")

        token = Token().load(csrfTokenId, objectId=False, force=True)
        if token is None:
            raise RestException('Invalid CSRF token (state="%s").' % state, code=403)

        try:
            user = User().load(token["userId"], force=True, exc=True)
        except (KeyError, ValidationException):
            raise RestException('No valid user (state="%s").' % state)

        Token().remove(token)

        if token["expires"] < datetime.datetime.utcnow():
            raise RestException('Expired CSRF token (state="%s").' % state, code=403)

        if not redirect:
            raise RestException('No redirect location (state="%s").' % state)

        return user, redirect

    @access.user
    @autoDescribeRoute(
        Description(
            "Get the list of enabled external account providers and their URLs."
        ).param(
            "redirect",
            "Where the user should be redirected upon completion"
            " of the OAuth2 flow.",
        )
    )
    def listAccounts(self, redirect):
        user = self.getCurrentUser()

        # OAuth providers ids with configured credentials that can be used for an external account
        enabled_providers_ids = set(
            Setting().get(OAuthConstants.PluginSettings.PROVIDERS_ENABLED)
        )

        # External account providers ids that we support, can be defined in plugin settings
        supported_providers_ids = set(self.supported_providers().keys())

        # Some special handling for DataONE, since we haven't done that for a while.
        # DataONE OAuth providers are "fake" </SHOCK> They don't really use OAuth, we derive it
        # from oauth.BaseProvider but only mock a limited set of methods that we need, e.g. to
        # get the name or the url for a coordinating node.
        dataone_providers_ids = {
            provider_name
            for provider_name, provider in self.supported_providers().items()
            if provider["type"] == "dataone"
        }

        # Supported providers that happen to be OAuth providers, need CSRF token for OAuth flow
        state = None
        if enabled_providers_ids & supported_providers_ids | dataone_providers_ids:
            state = self._createStateToken(redirect, user=user)

        # All supported *AND* enabled providers will be mapped by 'enabled_providers'
        # Note: apikey providers are always enabled.
        enabled_providers = {}
        for provider_name, provider in self.supported_providers().items():
            if provider["type"] in ("apikey", "dataone") or (
                provider["type"] == "bearer"
                and provider_name  # noqa
                in enabled_providers_ids & supported_providers_ids  # noqa
            ):
                enabled_providers[provider_name] = provider

        # We need to check which tokens user already has and modify the state of relevant
        # providers in 'enabled_providers' mapping. Two things happend here:
        #  1. State is modified to 'authorized' if user already has a token (for oauth and dataone)
        #     or target is appended to list of authorized apikey providers of a given type.
        #  2. An "action" url for a given provider is constructed, which is basically:
        #     * "how to authorize" if state is "unauthorized",
        #     * "how to revoke token" if state is "authorized".
        # NOTE: for Dataone, revoke url is bogus. There's no way to truly revoke it via their API.
        user_tokens = user.get("otherTokens", [])
        for user_token in user_tokens:
            try:
                enabled_provider = enabled_providers[user_token["provider"]]
                if user_token["token_type"].lower() in ("bearer", "dataone"):
                    provider = providers.idMap[user_token["provider"]]
                    enabled_provider["url"] = "/".join(
                        (getApiUrl(), "account", user_token["provider"], "revoke")
                    )
                    enabled_provider["state"] = "authorized"
                elif user_token["token_type"].lower() == "dataone-pre":
                    provider = providers.idMap[user_token["provider"]]
                    enabled_provider["state"] = "preauthorized"
                    enabled_provider["url"] = provider.getTokenUrl()
                else:
                    enabled_provider["targets"].append(
                        {
                            "resource_server": user_token["resource_server"],
                            "url": "/".join(
                                (
                                    getApiUrl(),
                                    "account",
                                    user_token["provider"],
                                    "revoke",
                                    "?resource_server={}".format(
                                        user_token["resource_server"]
                                    ),
                                )
                            ),
                        }
                    )
            except KeyError:
                pass

        for provider_name in (
            enabled_providers_ids & supported_providers_ids | dataone_providers_ids
        ):
            if not enabled_providers[provider_name]["url"]:
                provider = providers.idMap[provider_name]
                # NOTE: for dataone it's "/oauth?", so this should be noop
                enabled_providers[provider_name]["url"] = provider.getUrl(
                    state
                ).replace("%2Foauth%2F", "%2Faccount%2F")

        return list(enabled_providers.values())

    @access.user
    @autoDescribeRoute(
        Description("Revoke authorization for a given provider.")
        .param("provider", "The provider name.", paramType="path")
        .param("resource_server", "resource_server", required=False)
    )
    def revokeAccount(self, provider, resource_server):
        """Revoke account authorization.

        In case of OAuth use the proper flow (usually calling /revoke with refreshToken).
        In case of API Key, just drop it from the user model.
        In case of DataONE, hahaha you thought that's possible via API?! You silly goose!
        (User would have to clear cookies from CN or login to ORCID and deauthorize there)
        """
        try:
            provider_obj = self.supported_providers()[provider]
        except KeyError:
            raise RestException(
                "Invalid account provider (provider={})".format(provider)
            )

        if provider_obj["type"] == "apikey" and not resource_server:
            raise RestException(
                "Missing resource_server for apikey provider (provider={})".format(
                    provider
                )
            )

        user = self.getCurrentUser()
        user_tokens = user.get("otherTokens", [])

        # In case of APIKey, it's resource_server that's unique, not provider id
        if resource_server:
            key = "resource_server"
            target_value = resource_server
        else:
            key = "provider"
            target_value = provider

        token = next((_ for _ in user_tokens if _.get(key) == target_value), None)
        if token:
            user_tokens.remove(token)
            user["otherTokens"] = user_tokens
            User().save(user)

            if provider_obj["type"] == "bearer":
                oauth_provider = providers.idMap[provider]
                # NOTE: only ORCID has that implemented
                oauth_provider("").revokeToken(token)

    @access.public
    @autoDescribeRoute(
        Description("Callback called by OAuth providers.")
        .param("provider", "The provider name.", paramType="path")
        .param("state", "Opaque state string.", required=False)
        .param("code", "Authorization code from provider.", required=False)
        .param("error", "Error message from provider.", required=False),
        hide=True,
    )
    def callbackAccount(self, provider, state, code, error):
        """Classical OAuth callback endpoint that parses incoming token.

        The main difference between this callback and /oauth/:provider/callback
        is that we store the incoming bearer token into User model, instead of using it
        to actually login to Girder.
        Note: We conveniently hid the userId in the OAuth state.
        """
        if error is not None:
            raise RestException("Provider returned error: '%s'." % error, code=502)

        providerName = provider
        provider = providers.idMap.get(providerName)
        if not provider:
            raise RestException('Unknown provider "%s".' % providerName)

        self.requireParams({"state": state, "code": code})

        user, redirect = self._validateCsrfToken(state)
        providerObj = provider(cherrypy.url())
        new_token = providerObj.getToken(code)

        if "resource_server" not in new_token:
            new_token["resource_server"] = providerObj.getProviderName(external=False)
        new_token["provider"] = providerObj.getProviderName(external=False)

        user_tokens = user.get("otherTokens", [])
        for i, user_token in enumerate(user_tokens):
            if user_token["resource_server"] == new_token["resource_server"]:
                user_tokens[i] = new_token  # update token if found.
                break
        else:
            user_tokens.append(new_token)  # not found, append

        user["otherTokens"] = user_tokens
        user = User().save(user)
        self.sendAuthTokenCookie(user)
        raise cherrypy.HTTPRedirect(redirect)

    @access.user
    @autoDescribeRoute(
        Description("List possible resource servers for a Provider.").param(
            "provider", "The provider name.", paramType="path"
        )
    )
    def listTargetsForAccount(self, provider):
        try:
            targets = self.supported_apikey_flavors()[provider]
        except KeyError:
            raise RestException('Unknown provider "%s".' % provider)

        user = self.getCurrentUser()
        user_tokens = user.get("otherTokens", [])
        for user_token in user_tokens:
            if user_token.get("provider") != provider:
                continue
            try:
                targets.remove(user_token["resource_server"])
            except ValueError:
                pass
        return targets

    @access.user
    @autoDescribeRoute(
        Description("Add apikey for a given Provider.")
        .param("provider", "The provider name.", paramType="path")
        .param("resource_server", "resource_server", required=True)
        .param("key", "API Key for resource server", required=True)
        .param(
            "key_type",
            "Type of the API key.",
            required=False,
            default="apikey",
            enum=["apikey", "dataone"],
        )
    )
    def addAccountKey(self, provider, resource_server, key, key_type):
        user = self.getCurrentUser()

        try:
            if key_type == "apikey":
                key_provider = self.supported_apikey_flavors()[provider]
                if resource_server not in key_provider:
                    raise RestException(
                        'Unsupported resource server "%s".' % resource_server
                    )
            else:
                key_provider = providers.idMap[provider]
                resource_server = urlparse(key_provider.get_cn()).netloc

        except KeyError:
            raise RestException('Unknown provider "%s".' % provider)

        Verificators[provider](resource_server, key).verify()

        user_tokens = user.get("otherTokens", [])
        for i, user_token in enumerate(user_tokens):
            if user_token["resource_server"] == resource_server:
                user_tokens[i].update(  # update token if found.
                    {"access_token": key, "token_type": key_type}
                )
                break
        else:
            user_tokens.append(
                {
                    "resource_server": resource_server,
                    "access_token": key,
                    "token_type": key_type,
                    "provider": provider,
                }
            )  # not found, append

        user["otherTokens"] = user_tokens
        User().save(user)


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

img = cv2.imread('D:/Program Files/opencv-master/samples/data/smarties.png', cv2.IMREAD_GRAYSCALE)

_, mask = cv2.threshold(img, 220, 255, cv2.THRESH_BINARY_INV)

kernal = np.ones((5,5), np.uint8)
print(kernal)
dialation = cv2.dilate(mask, kernal, iterations = 2)
erosion =  cv2.erode(mask, kernal, iterations=1)
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernal)  # erosion ->  dilation
closing = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernal)  # dialation  ->   erosion
gd = cv2.morphologyEx(mask, cv2.MORPH_GRADIENT, kernal)
th = cv2.morphologyEx(mask, cv2.MORPH_TOPHAT, kernal) # difference between input image and opening image

titles  = ['image', 'mask', 'Dialate', 'Erode', 'open','closing', 'gd', 'th']
images = [img, mask, dialation, erosion, opening, closing, gd, th]

for i in range(8):
    plt.subplot(2, 4, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])

plt.show()

'''
  Smoothing and blurring

  1. Homogeneous filter. each output pixel is the  mean of kernal neighbours
  2. bluring
  3, Gaussian filter : different weight kernal , Gaussian distribution
  4. median filter: replace each pixel's value with median of its neighbour pixels, deal with "sault and pepper nosoe "

'''
img = cv2.imread('D:/Program Files/opencv-master/samples/data/HappyFish.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

kernel = np.ones((5, 5), np.float32)/25
dst = cv2.filter2D(img, -1, kernel)
blur = cv2.blur(img,(5,5))
gblur = cv2.GaussianBlur(img, (5,5), 0)
median = cv2.medianBlur(img, 5)
bilatralFilter = cv2.bilateralFilter(img, 9, 75, 75)   # remove noise and remain border sharp

titles  = ['image', '2D_conv', 'blur', 'gblur', 'median', 'Bilateral']
images = [img, dst, blur, gblur, median, bilatralFilter]

for i in range(len(images)):
    plt.subplot(2, 3, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])

plt.show()


import json

import cv2
from django.http import HttpResponse, StreamingHttpResponse, JsonResponse
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
import time
import os

from django.core import serializers

from Camera.settings import MEDIA_ROOT

from django.views.decorators.http import require_http_methods

from .models import Patient

cameraWriter = None  # 视频写入对象
camera = None  # 视频
record = False  # 录屏信号
flag = False
sign = False
video_path = None
temp_name = None  # 传递病人姓名给录像文件
temp_id = None  # 传递病人 ID 给录像文件

def generate_frames():
    global camera, record, flag, cameraWriter, video_path, temp_name, temp_id

    while True:
        if camera is not None:
            success, frame = camera.read()
            if record:
                if flag is False:
                    now = time.strftime("%Y-%m-%d-%H_%M_%S",
                                        time.localtime(time.time()))
                    if sign is True:
                        video_name = "media/" + r"temp.mp4"
                    else:
                        video_name = "media/" + temp_id + r"_" + temp_name + r"_" + now + r".mp4"
                        video_path = os.path.join(MEDIA_ROOT, now + r".mp4")
                    fps = camera.get(5)
                    size = (int(camera.get(cv2.CAP_PROP_FRAME_WIDTH)),
                            int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT)),)
                    cameraWriter = cv2.VideoWriter(
                        video_name, cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), fps, size)
                    flag = True
                cameraWriter.write(frame)  # 给新视频添加新帧
            if not success:
                break
            else:
                # 将处理后的帧转换为JPG格式
                ret, frame = cv2.imencode('.jpeg', frame)
                if ret:
                    yield (b'--frame\r\n'
                           b'Content-Type: image/jpeg\r\n\r\n' + frame.tobytes() + b'\r\n')
        else:
            break

@csrf_exempt
def start_camera(request):
    global camera
    if camera is None:
        camera = cv2.VideoCapture("rtsp://admin:linke2023@192.168.2.102/ch1/sub/av_stream?tcp")
    return StreamingHttpResponse(generate_frames(), content_type='multipart/x-mixed-replace; boundary=frame')

@csrf_exempt
def stop_camera(request):
    global camera, record, flag, sign
    if camera is not None:
        camera.release()
        camera = None
    record = False
    flag = False
    sign = False
    return HttpResponse("Camera stopped")

@csrf_exempt
def start_record(request):
    global record
    record = True
    return HttpResponse("Recording started")

@csrf_exempt
def stop_record(request):
    global record, flag, sign
    record = False
    flag = False
    time.sleep(0.1)
    sign = True
    record = True
    time.sleep(0.1)
    record = False
    flag = False
    sign = False
    return HttpResponse("Recording stopped")

@require_http_methods(["GET"])
def save_patient(request):
    global temp_name, temp_id
    response = {}
    try:
        patient_name = request.GET.get("patient_name")
        patient_id = request.GET.get("patient_id")
        if Patient.objects.filter(patientId=patient_id).exists():
            response["respMsg"] = "ID exists"
            response["respCode"] = "999999"
            return JsonResponse(response)
        temp_name = patient_name
        temp_id = patient_id
        # 创建病人对象并保存到数据库
        patient = Patient(patientId=patient_id)
        patient.patientName = patient_name
        patient.save()
        response["respMsg"] = "success"
        response["respCode"] = "000000"
    except Exception as e:
        response["respMsg"] = str(e)
        response["respCode"] = "999999"
    return JsonResponse(response)

@require_http_methods(["GET"])
def inquire_patient(request):
    response = {}
    try:
        patient_name = request.GET.get("patient_name")
        patient = Patient.objects.filter(patientName=patient_name)
        response["list"] = json.loads(serializers.serialize("json", patient))
        response["respMsg"] = "success"
        response["respCode"] = "000000"
    except Exception as e:
        response["respMsg"] = str(e)
        response["respCode"] = "999999"
    return JsonResponse(response)

@require_http_methods(["GET"])
def show_patient(request):
    response = {}
    try:
        patient = Patient.objects.filter()
        response["list"] = json.loads(serializers.serialize("json", patient))
        response["respMsg"] = "success"
        response["respCode"] = "000000"
    except Exception as e:
        response["respMsg"] = str(e)
        response["respCode"] = "999999"
    return JsonResponse(response)

@require_http_methods(["GET"])
def update_patient(request):
    global video_path
    response = {}
    try:
        patient_id = request.GET.get("patient_id")
        # 获取指定的条目
        patient = Patient.objects.get(patientId=patient_id)
        filename = os.path.basename(video_path)
        patient.videoUrl = "/root/autodl-tmp/media" + filename
        #  video_url = request.build_absolute_uri(video_path)
        # 更新相关字段
        #  patient.videoUrl = video_url
        # 更新其他字段

        # 保存更新后的条目
        patient.save()
        response["respMsg"] = "success"
        response["respCode"] = "000000"
    except Patient.DoesNotExist as e:
        response["respMsg"] = str(e)
        response["respCode"] = "999999"
    return JsonResponse(response)

@require_http_methods(["GET"])
def disease_kind(request):
    response = {}
    try:
        patient_disease = request.GET.get("patient_disease")
        patient_id = int(request.GET.get("patient_id"))
        # 获取指定的条目
        patient = Patient.objects.get(patientId=patient_id)
        # 更新相关字段
        patient.diseaseKind = patient_disease
        # 更新其他字段

        # 保存更新后的条目
        patient.save()
        response["respMsg"] = "success"
        response["respCode"] = "000000"
    except Patient.DoesNotExist as e:
        response["respMsg"] = str(e)
        response["respCode"] = "999999"
    return JsonResponse(response)

@require_http_methods(["GET"])
def upload_images(request):
    response = {}
    image_path = MEDIA_ROOT
    try:
        for filename in os.listdir(image_path):
            if filename.endswith(".jpg"):
                image_url = image_path + f'{filename}'  # 需要根据实际情况修改 URL
                base_name, extension = os.path.splitext(filename)
                kind = base_name.split('_')[0]
                patient_id = base_name.split('_')[1]
                patient_name = base_name.split('_')[2]
                try:
                    patient = Patient.objects.get(patientId=patient_id)
                except Patient.DoesNotExist:
                    # 如果条目不存在，则创建一个新条目
                    patient = Patient.objects.create(patientId=patient_id)
                    patient.patientName = patient_name
                if kind == "face":
                    patient.faceUrlSelf = image_url
                else:
                    patient.tongueUrlSelf = image_url
                patient.save()
        response["respMsg"] = "success"
        response["respCode"] = "000000"
    except Exception as e:
        response["respMsg"] = str(e)
        response["respCode"] = "999999"
    return JsonResponse(response)

@require_http_methods(["GET"])
def delete_items(request):
    patient_id = request.GET.get("patient_id")  # 接收前端传递的被选中的条目的ID列表
    response = {}
    try:
        item = Patient.objects.get(patientId=patient_id)
        item.delete()
        response["respMsg"] = "success"
        response["respCode"] = "000000"
    except Patient.DoesNotExist as e:
        response["respMsg"] = str(e)
        response["respCode"] = "999999"
    return JsonResponse(response)


#!/usr/bin/env python
# This final piece fo skeleton code will be centred around gettign the students to follow a colour and stop upon sight of another one.

from __future__ import division
import cv2
import numpy as np
import rospy
import sys

from geometry_msgs.msg import Twist, Vector3
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError

class colourIdentifier():

	def __init__(self):
		# Initialise a publisher to publish messages to the robot base
		self.pub = rospy.Publisher('mobile_base/commands/velocity', Twist, queue_size=10)
		# We covered which topic receives messages that move the robot in the 2nd Lab Session

		# Initialise any flags that signal a colour has been detected in view
		self.red_detected = 0
		self.blue_detected = 0
		self.green_detected = 0

		# Initialise the value you wish to use for sensitivity in the colour detection (10 should be enough)
		self.sensitivity = 10
		# Initialise some standard movement messages such as a simple move forward and a message with all zeroes (stop)s
		# Remember to initialise a CvBridge() and set up a subscriber to the image topic you wish to use

		self.bridge = CvBridge()
		rospy.Subscriber('camera/rgb/image_raw', Image, self.callback)

	def callback(self, data):
		# Convert the received image into a opencv image
		# But remember that you should always wrap a call to this conversion method in an exception handler
		try:
			cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8")
		except CvBridgeError as e:
			print(e)

		# Set the upper and lower bounds for the two colours you wish to identify

		hsv_green_lower = np.array([60 - self.sensitivity, 100, 100])
		hsv_green_upper = np.array([60 + self.sensitivity, 255, 255])

		hsv_red_lower = np.array([10 - self.sensitivity, 100, 100])
		hsv_red_upper = np.array([5 + self.sensitivity, 255, 255])

		hsv_blue_lower = np.array([110 - self.sensitivity, 100, 100])
		hsv_blue_upper = np.array([120 + self.sensitivity, 255, 255])

		# Convert the rgb image into a hsv image
		hsv = cv2.cvtColor(cv_image, cv2.COLOR_BGR2HSV)

		maskg = cv2.inRange(hsv, hsv_green_lower, hsv_green_upper)
		maskr = cv2.inRange(hsv, hsv_red_lower, hsv_red_upper)
		maskb = cv2.inRange(hsv, hsv_blue_lower, hsv_blue_upper)

		maskForRed = cv2.bitwise_and(cv_image, cv_image, mask=maskr)
		red_and_green = cv2.bitwise_or(maskg, maskr)
		all_colors = cv2.bitwise_or(red_and_green, maskb)

		kernelOpen = np.ones((5, 5))
		kernelClose = np.ones((20, 20))

		maskOpen = cv2.morphologyEx(all_colors, cv2.MORPH_OPEN, kernelOpen)
		maskClose = cv2.morphologyEx(maskOpen, cv2.MORPH_CLOSE, kernelClose)

		# find edges
		edged = cv2.Canny(all_colors, 50, 200)

		try:
			contours, hierarchy = cv2.findContours(maskClose.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
		except Exception as e:
			print(e)

		hue_of_color_to_track = 60

		if len(contours) > 0:
			sorted_contours = sorted(contours, key=cv2.contourArea, reverse=True)
			max_area = cv2.contourArea(sorted_contours[0])
			print(str(max_area))

			largest_area_center_point = cv2.moments(sorted_contours[0])
			cx = int(largest_area_center_point['m10'] / largest_area_center_point['m00'])
			cy = int(largest_area_center_point['m01'] / largest_area_center_point['m00'])

			height, width = cv_image.shape[:2]

			color_at_largest_area_center = cv_image[cy, cx]

			B = color_at_largest_area_center[0]
			G = color_at_largest_area_center[1]
			R = color_at_largest_area_center[2]

			rect = cv2.boundingRect(sorted_contours[0])
			x, y, w, h = rect

			if G > 0:
				self.blue_detected = 0
				self.red_detected = 0
				self.green_detected = 1
				print("Color is red")
			elif R > 0:
				self.blue_detected = 0
				self.red_detected = 1
				self.green_detected = 0
				print("Color is green")
			elif B > 0:
				self.blue_detected = 1
				self.red_detected = 0
				self.green_detected = 0
				print("Color is Blue")
			move = Twist()
			# Check if a flag has been set for the stop message
			if self.green_detected == 1:
				if max_area > 35000:
					# Too close to object, need to move backwards
					# linear = positive
					# angular = radius of minimum enclosing circle
					err = cx - (width / 2)
					move.linear.x = -0.1
					move.angular.z = -float(err) / 100
				elif max_area < 30000:
					# Too far away from object, need to move forwards
					# linear = positive
					# angular = radius of minimum enclosing circle
					err = cx - (width / 2)
					move.linear.x = 0.1
					move.angular.z = -float(err) / 100
					print(str(move))
				self.pub.publish(move)
			# self.<publisher_name>.publish(<Move>)
			else:
				self.pub.publish(Twist())

			# Be sure to do this for the other colour as well
			# Show the resultant images you have created. You can show all of them or just the end result if you wish to.
			cv2.drawContours(cv_image.copy(), contours, -1, (0, 255, 0), 3)
			# cv2.drawContours(cv_image, sorted_contours[0], -1, (255, 0, 0), 3)
			cv2.rectangle(cv_image, (x, y), (x + w, y + h), (255, 0, 0), 2)
			# cv2.circle(cv_image, (cx, cy), 10, (0, 0, 0), -1)
			cv2.imshow('Circle', cv_image)
			cv2.waitKey(3)
		else:
			self.pub.publish(Twist())
			print("no objects detected")

# Create a node of your class in the main and ensure it stays up and running
# handling exceptions and such
def main(args):
	# Instantiate your class
	# And rospy.init the entire node
	rospy.init_node('colourIdentifier', anonymous=True)
	cI = colourIdentifier()
	# Ensure that the node continues running with rospy.spin()
	# You may need to wrap rospy.spin() in an exception handler in case of KeyboardInterrupts
	try:
		rospy.spin()
	except KeyboardInterrupt:
		print("Shutting down")
	# Remember to destroy all image windows before closing node
	cv2.destroyAllWindows()

# Check if the node is executing in the main path
if __name__ == '__main__':
	main(sys.argv)


from collections import deque
from sys import stdin

instructionsNum = int(stdin.readline().strip())

deq = deque()
while instructionsNum:
    instruction = list(stdin.readline().strip().split())
    if len(instruction)>1:
        if instruction[0] == 'push':
            deq.append(instruction[1])
    else:
        if instruction[0] == 'front':
            if len(deq):
                print(deq[0])
            else:
                print(-1)
        elif instruction[0] == 'pop':
            if len(deq):
                print(deq.popleft())
            else:
                print(-1)
        elif instruction[0] == 'size':
            print(len(deq))
        elif instruction[0] == 'empty':
            if len(deq):
                print(0)
            else:
                print(1)
        elif instruction[0] == 'back':
            if len(deq):
                print(deq[-1])
            else:
                print(-1)
    instructionsNum -= 1


import click

@click.command()
@click.option('-n', help='# of nomral users', type=click.INT)
@click.option('-m', help='# of attackers', type=click.INT)
@click.option('-r', help='rate of attack')
def check_data(n, m, r):
    domain='10.0.2.'
    # Check syn/ack
    ip=set()
    f=open('n%d_m%d_r%s.txt' % (n, m, r))
    for packet in f.readlines():
        (timestamp, flag, src_ip, dst_ip) = [t(s) for t,s in zip((float,str, str, str),packet.split())]
        if(flag=='SA'):
            ip.add(dst_ip)
    print("%d/%d" % (len(ip), n+m))

if __name__ == '__main__':
    check_data()


import glob
import os
import pickle
import zipfile

import editdistance
import numpy as np
import shapely
from shapely.geometry import MultiPoint, Polygon

from multiplexer.evaluation.utils.weighted_editdistance import weighted_edit_distance

# from tqdm import tqdm

def append_txt_to_zip(zip_file, txt_file):
    with zipfile.ZipFile(zip_file, "a") as zipf:
        zipf.write(txt_file, os.path.basename(txt_file))

def list_from_str(st):
    line = st.split(";")
    segms = line[1].split(",")
    scores = line[2].split(",")
    new_line = (
        [float(a) for a in segms]
        + [float(scores[-4])]
        + [scores[-5]]
        + [scores[-6]]
        + [float(scores[-3])]
        + [float(scores[-2])]
        + [scores[-1]]
    )
    return new_line

def polygon_from_list(line):
    """
    Create a shapely polygon object from gt or dt line.
    """
    polygon_points = np.array(line).reshape(-1, 2)
    polygon = Polygon(polygon_points).convex_hull
    return polygon

def polygon_iou(list1, list2):
    """
    Intersection over union between two shapely polygons.
    """
    polygon_points1 = np.array(list1).reshape(-1, 2)
    poly1 = Polygon(polygon_points1).convex_hull
    polygon_points2 = np.array(list2).reshape(-1, 2)
    poly2 = Polygon(polygon_points2).convex_hull
    union_poly = np.concatenate((polygon_points1, polygon_points2))
    if not poly1.intersects(poly2):  # this test is fast and can accelerate calculation
        iou = 0
    else:
        try:
            inter_area = poly1.intersection(poly2).area
            # union_area = poly1.area + poly2.area - inter_area
            union_area = MultiPoint(union_poly).convex_hull.area
            iou = float(inter_area) / (union_area + 1e-6)
        except shapely.geos.TopologicalError:
            print("shapely.geos.TopologicalError occured, iou set to 0")
            iou = 0
    return iou

def nms(boxes, overlap):
    rec_scores = [b[-6] for b in boxes]
    indices = sorted(range(len(rec_scores)), key=lambda k: -rec_scores[k])
    box_num = len(boxes)
    nms_flag = [True] * box_num
    for i in range(box_num):
        ii = indices[i]
        if not nms_flag[ii]:
            continue
        for j in range(box_num):
            jj = indices[j]
            if j == i:
                continue
            if not nms_flag[jj]:
                continue
            box1 = boxes[ii]
            box2 = boxes[jj]
            box1_score = rec_scores[ii]
            box2_score = rec_scores[jj]
            poly1 = polygon_from_list(box1[0:-6])
            poly2 = polygon_from_list(box2[0:-6])
            iou = polygon_iou(box1[0:-6], box2[0:-6])
            thresh = overlap

            if iou > thresh:
                if box1_score > box2_score:
                    nms_flag[jj] = False
                if box1_score == box2_score and poly1.area > poly2.area:
                    nms_flag[jj] = False
                if box1_score == box2_score and poly1.area <= poly2.area:
                    nms_flag[ii] = False
                    break

    return nms_flag

def packing(save_dir, cache_dir, pack_name):
    # files = os.listdir(save_dir)
    if not os.path.exists(cache_dir):
        os.makedirs(cache_dir)
    os.system("zip -r -q -j " + os.path.join(cache_dir, pack_name) + " " + save_dir + "/*")

def load_lexicon_pairs(pair_path):
    pairs = {}
    with open(pair_path, "r") as pair_list:
        for line in pair_list.readlines():
            line = line.strip()
            word_pair = line.split(" ")
            assert len(word_pair) == 2, "More than two words found in a pair in {}: {}".format(
                pair_path, line
            )
            word = word_pair[0].strip().upper()
            if word_pair[1].lstrip() != word_pair[1]:
                print("[Warning] Strange character found in {}: {}", line)
            word_gt = word_pair[1].strip()
            pairs[word] = word_gt

    return pairs

def prepare_results_for_evaluation(
    results_dir,
    lexicon=None,
    cache_dir=None,
    score_det=0.5,
    score_rec_seq=0.5,
    score_rec_charmask=0.5,
    overlap=0.2,
    weighted_ed=True,
    use_rec_seq=False,
    use_rec_charmask=False,
    lexicon_path=None,
    lexicon_pair_path=None,
):
    """
    results_dir: result directory
    score_det: score of detection bounding box
    score_rec_seq: score of the sequence recognition branch
    score_rec_charmask: score of the character mask recognition branch
    overlap: overlap threshold used for nms
    use_rec_seq: use the recognition result of sequence branch
    use_rec_charmask: use the recognition result of the mask

    Note: when use_rec_seq and use_rec_charmask are both enabled,
        use both the recognition result of the mask and
        the sequence branches, and choose the one with higher score.
    """
    print(
        "score_det:",
        score_det,
        "score_rec_seq:",
        score_rec_seq,
        "score_rec_charmask:",
        score_rec_charmask,
        "overlap:",
        overlap,
        "lexicon:",
        lexicon,
        "weighted_ed:",
        weighted_ed,
        "use_rec_seq:",
        use_rec_seq,
        "use_rec_charmask:",
        use_rec_charmask,
    )
    if not os.path.exists(cache_dir):
        os.makedirs(cache_dir)

    prefix = ""
    if use_rec_seq:
        prefix += "seq"
    if use_rec_charmask:
        prefix += "charmask"

    nms_dir = os.path.join(
        cache_dir,
        "{}_det{}_charmask{}_seq{}_iou{}_lex-{}".format(
            prefix, score_det, score_rec_charmask, score_rec_seq, overlap, lexicon
        ),
    )
    if not os.path.exists(nms_dir):
        os.mkdir(nms_dir)
    if lexicon == "weak":
        with open(lexicon_path, "r") as lexicon_fid:
            vocabularies = [voc.strip() for voc in lexicon_fid.readlines()]

        pairs = load_lexicon_pairs(lexicon_pair_path)

    count = 0
    for res_file in glob.glob("{}/*.txt".format(results_dir)):
        count += 1
        print("[{}/300] Processing {}".format(count, res_file))
        result_path = os.path.join(results_dir, res_file)
        assert os.path.isfile(result_path), "{}/{}".format(results_dir, res_file)
        if os.path.isfile(result_path):
            with open(result_path, "r") as f:
                dt_lines = [a.strip() for a in f.readlines()]
            dt_lines = [list_from_str(dt) for dt in dt_lines]
        else:
            dt_lines = []
        dt_lines = [
            dt
            for dt in dt_lines
            if dt[-2] > score_rec_seq and dt[-3] > score_rec_charmask and dt[-6] > score_det
        ]
        nms_flag = nms(dt_lines, overlap)
        boxes = []
        for k in range(len(dt_lines)):
            dt = dt_lines[k]
            if nms_flag[k]:
                if dt not in boxes:
                    boxes.append(dt)

        final_res_path = os.path.join(nms_dir, os.path.basename(res_file))

        with open(final_res_path, "w") as f:
            for g in boxes:
                gt_coors = [int(b) for b in g[0:-6]]
                pkl_name = g[-1]
                if not os.path.isfile(pkl_name):
                    pkl_name = os.path.join(results_dir, os.path.basename(pkl_name))
                with open(pkl_name, "rb") as input_file:
                    dict_scores = pickle.load(input_file)
                # print("[Debug] Loaded {}".format(pkl_name))
                # print("[Debug] dict_scores: {}".format(dict_scores))

                if use_rec_charmask and use_rec_seq:
                    if g[-2] > g[-3]:
                        word = g[-5]
                        scores = dict_scores["seq_char_scores"][:, 1:-1].swapaxes(0, 1)
                    else:
                        word = g[-4]
                        scores = dict_scores["seg_char_scores"]
                elif use_rec_seq:
                    word = g[-5]
                    scores = dict_scores["seq_char_scores"][:, 1:-1].swapaxes(0, 1)
                else:
                    word = g[-4]
                    scores = dict_scores["seg_char_scores"]

                if lexicon == "none":
                    match_word = word
                    match_dist = 0.0
                else:
                    match_word, match_dist = find_match_word(
                        word, pairs, scores, weighted_ed, vocabularies
                    )

                if match_dist < 1.5 or lexicon == "none":
                    if match_dist > 0:
                        print(
                            "[Info] Corrected word {} to {} with edit dist {:.2f}".format(
                                word, match_word, match_dist
                            )
                        )
                    gt_coor_strs = [str(a) for a in gt_coors]
                    f.write(",".join(gt_coor_strs) + "\n")
                    f.write(match_word + "\n")
                else:
                    print(
                        "[Info] Filtered word {} ({} with edit dist {:.2f})".format(
                            word, match_word, match_dist
                        )
                    )

            print("[Debug] Saved {}".format(final_res_path))

    zip_name = "{}_det{}_charmask{}_seq{}_iou{}_lex-{}.zip".format(
        prefix, score_det, score_rec_charmask, score_rec_seq, overlap, lexicon
    )

    packing(nms_dir, cache_dir, zip_name)
    submit_file_path = os.path.join(cache_dir, zip_name)
    print("[Info] Final submission file: {}".format(submit_file_path))
    return submit_file_path

def find_match_word(rec_str, pairs, scores_numpy, weighted_ed=False, vocabularies=None):
    rec_str = rec_str.upper()
    dist_min = 100
    dist_min_pre = 100
    match_word = ""
    match_dist = 100
    if not weighted_ed:
        for word in vocabularies:
            word = word.upper()
            ed = editdistance.eval(rec_str, word)
            # length_dist = abs(len(word) - len(rec_str))
            # dist = ed + length_dist
            dist = ed
            if dist < dist_min:
                dist_min = dist
                match_word = pairs[word]
                match_dist = dist
        return match_word, match_dist
    else:
        small_lexicon_dict = {}
        for word in vocabularies:
            word = word.upper()
            ed = editdistance.eval(rec_str, word)
            small_lexicon_dict[word] = ed
            dist = ed
            if dist < dist_min_pre:
                dist_min_pre = dist
        small_lexicon = []
        for word in small_lexicon_dict:
            if small_lexicon_dict[word] <= dist_min_pre + 2:
                small_lexicon.append(word)

        for word in small_lexicon:
            word = word.upper()
            ed = weighted_edit_distance(rec_str, word, scores_numpy)
            dist = ed
            if dist < dist_min:
                dist_min = dist
                match_word = pairs[word]
                match_dist = dist
        return match_word, match_dist


from collections import Counter

for _ in range(int(input())):
    n = int(input())
    a = [int(d) for d in input().split()]
    m = int(input())
    b = [int(d) for d in input().split()]
    p = Counter()
    for i in range(n + 1):
        for j in range(m + 1):
            p[i + j] += a[i] * b[j]
    keys = max([k for k in p.keys() if p[k] != 0])
    print(keys)
    print(" ".join([str(p[k]) for k in range(keys + 1)]))


from xml.etree import ElementTree
from xml.etree.ElementTree import Element, SubElement
from lxml import etree

XML_EXT = '.xml'
class HRSCReaderCls:
    """processing anns with hrsc2016 dataset
        L1: ship
        L2: aircraft carrier,warcraft,merchant ship
        L3: 29 classes
    get the dict key for class_id, val for class_Name, acc Li
    only extract layer, others assign parent
    sysdata.xml format:
        HRS_Class_ID: parent_id
        Class_ID: class_id
        Class_Layer: layer [0,1,2 reps L1, L2, L3]
    """
    def __init__(self, clsPath, layer=0):
        self.clsPath = clsPath
        self.clsDict = {}
        self.layer = layer
        self.MaxImgWidth = 0
        self.MinImgWidth = 10240
        assert self.layer in [0,1,2],  "Unsupport layer, only for 0,1,2"

        self.__getClsDict()

    def getclsDict(self):
        return self.clsDict

    def __getClsDict(self):
        assert self.clsPath.endswith(XML_EXT), "Unsupport file format"
        parser = etree.XMLParser(encoding='utf-8')
        xmltree = ElementTree.parse(self.clsPath, parser=parser).getroot()
        for child in xmltree:
            tag = child.tag

            if tag == "HRSC_Classes":
                for HRSC_Class in child:
                    class_id = parent_id = 0
                    for class_elem in HRSC_Class:
                        if class_elem.tag == "Class_Layer":
                            layer = int(class_elem.text)
                        elif class_elem.tag == "Class_ID":
                            class_id = int(class_elem.text)
                        elif class_elem.tag == "HRS_Class_ID":
                            parent_id = class_elem.text
                        elif class_elem.tag == "Class_Name":
                            class_name = class_elem.text

                    # Only handle class layer 1
                    # assign layer 2 class id to layer 1
                    if class_id not in self.clsDict:
                        if self.layer == 0:
                            self.clsDict[class_id] = 'ship'
                        elif self.layer == 1:
                            if layer in [0,1]:
                                self.clsDict[class_id] = class_name
                            elif layer == 2:
                                self.clsDict[class_id] = self.clsDict[int(parent_id)]
                        elif self.layer == 2:
                            self.clsDict[class_id] = class_name

    def parseXML(self, xmlFile):
        """return rbbox from annotation file(*.xml)
        rbbox shapes: [cx,cy,w,h,theta]
        """
        assert xmlFile.endswith(XML_EXT), "Unsupport file format"

        shapes = []
        #[class_id, robndbox]

        parser = etree.XMLParser(encoding='utf-8')
        xmltree = ElementTree.parse(xmlFile, parser=parser).getroot()

        width = int(xmltree.find('Img_SizeWidth').text)
        height = int(xmltree.find('Img_SizeHeight').text)
        self.MaxImgWidth = max(self.MaxImgWidth,width,height)
        self.MinImgWidth = min(self.MinImgWidth,width,height)
        HRSC_Objects = xmltree.find('HRSC_Objects')
        for HRSC_Object in HRSC_Objects.findall('HRSC_Object'):
            class_id = int(HRSC_Object.find('Class_ID').text)

            mbox_cx = float(HRSC_Object.find('mbox_cx').text)
            mbox_cy = float(HRSC_Object.find('mbox_cy').text)
            mbox_w = float(HRSC_Object.find('mbox_w').text)
            mbox_h = float(HRSC_Object.find('mbox_h').text)
            mbox_ang = float(HRSC_Object.find('mbox_ang').text)

            robndbox = [mbox_cx, mbox_cy, mbox_w, mbox_h, mbox_ang]

            shapes.append([class_id, robndbox])

        return shapes


import warnings
warnings.simplefilter('ignore')

import numpy as np
from energyflow.utils import to_categorical
from energyflow.archs import EFN
import os.path as osp, os
from time import strftime
from tensorflow.keras.callbacks import ModelCheckpoint

def load(npz):
    d = np.load(npz)
    return d['X'], d['y'], d['inpz']

def main():
    X_train, y_train, inpz_train = load('data/train/merged.npz')
    X_test, y_test, inpz_test = load('data/test/merged.npz')

    # One-hot encoded
    Y_train = to_categorical(y_train, 2)
    Y_test = to_categorical(y_test, 2)

    Phi_sizes, F_sizes = (100, 100, 128), (100, 100, 100)
    efn = EFN(input_dim=2, Phi_sizes=Phi_sizes, F_sizes=F_sizes)

    efn.load_weights('ckpts_Apr06_134930/ckpt-40-val_acc-0.73.hdf5')

    ckpt_dir = strftime('ckpts_%b%d_%H%M%S')
    if not osp.isdir(ckpt_dir): os.makedirs(ckpt_dir)

    checkpoint_callback = ModelCheckpoint(
        ckpt_dir + '/ckpt-{epoch:02d}-val_acc-{val_acc:.3f}.hdf5',
        monitor='val_acc', verbose=1, save_best_only=False, mode='max'
        )
    best_checkpoint_callback = ModelCheckpoint(
        ckpt_dir + '/ckpt-best.hdf5',
        monitor='val_acc', verbose=1, save_best_only=True, mode='max'
        )

    efn.fit(
        [X_train[:,:,0], X_train[:,:,1:]], Y_train,
        epochs=40,
        batch_size=64,
        validation_data=([X_test[:,:,0], X_test[:,:,1:]], Y_test),
        verbose=1,
        callbacks=[checkpoint_callback, best_checkpoint_callback]
        )

if __name__ == '__main__':
    main()


from __future__ import unicode_literals
import frappe
from frappe import _
from frappe import msgprint
from frappe.model.document import Document
from frappe.utils import money_in_words

class SalesInvoiceTool(Document):
	def validate(self):
		items = []
		for d in self.sales_invoice_tool_item:
			item_li = {"item_code": d.item_code,"qty": d.qty,"rate": d.rate,"warehouse":d.warehouse}
			items.append(item_li)
		sales_invoice = frappe.get_doc({
		"doctype": "Sales Invoice",
		"customer": self.customer_name,
		"posting_date": self.posting_date,
		"base_discount_amount":self.additional_discount_amount,
		"additional_discount_percentage":self.additional_discount_percentage,
		"apply_discount_on":"Net Total",
		"total":self.total,
		"taxes_and_charges":self.taxes,
		"created_from":self.name,
		"items": items
		})
		sales_invoice.insert(ignore_permissions=True)
		sales_invoice.save()

@frappe.whitelist(allow_guest=True)
def getStockBalance(item_code, warehouse):
	balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry`
		where item_code=%s and warehouse=%s and is_cancelled='No'
		order by posting_date desc, posting_time desc, name desc
		limit 1""",(item_code,warehouse))
	return balance_qty[0][0] if balance_qty else 0.0

@frappe.whitelist(allow_guest=True)
def insert_data(doctype, name):
	query="select name, grand_total from `tabSales Invoice` where created_from = '"+str(name)+"';"
	li=[]
	dic=frappe.db.sql(query, as_dict=True)
	for i in dic:
		name,grand_total=i['name'],i['grand_total']
		li.append([name,grand_total])
	return li