from math import ceil

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

from atlas_scorer.score import ConfusionMatrixResult

def confusion_matrix_plot(c_matrix: ConfusionMatrixResult,
                          color_lims=None,
                          ax=None):
    """
    Visualize a ConfusionMatrixResult as a table

    Args:
        c_matrix (ConfusionMatrixResult): The confusion matrix
        color_lims (List[float] or None): A list of length 2, containing the
            min and max values for clipping the colors in the displayed table.
            If color_lims is None, the default values are [0, 1] if the
            c_matrix.normalize is True, and [0, count_matrix.max()] otherwise.
        ax (mpl.axes.Axes, or None): Axes instance (im_ax.axes).  Default is
            None.

    Returns:
        (ConfusionMatrixResult): The input ConfusionMatrixResult with the
            following attributes set:
                im_ax (mpl.image.AxesImage): AxesImage instance
                ax (mpl.axes.Axes): Axes instance (im_ax.axes)
                fig (mpl.figure.Figure): Figure instance (im_ax.figure)
                text_annots (List[mpl.text.Text]): Text annotations for each
                    cell

    """
    n_decls = c_matrix.num_decls
    n_annots = c_matrix.num_annots
    u_decl_classes = c_matrix.u_decl_classes
    u_annot_classes = c_matrix.u_annot_classes
    count_matrix = c_matrix.count_matrix

    if color_lims is None:
        color_lims = [0, 1] if c_matrix.normalize else [0, count_matrix.max()]

    color_thresh = np.mean(color_lims)

    decl_labels = [f'{u}[{n_decls[i][0]:.0f}]' for i, u in
                   enumerate(u_decl_classes)]
    annot_labels = [f'{u}[{n_annots[i][0]:.0f}]' for i, u in
                    enumerate(u_annot_classes)]

    # Plot the confusion matrix
    im, _ = heatmap(
        count_matrix, row_labels=annot_labels, col_labels=decl_labels,
        xlabel='Declaration', ylabel='Truth', xticks_rotation=45,
        display_cbar=False, clims=color_lims, ax=ax)

    # Annotate the confusion matrix
    text_annots = annotate_heatmap(im, threshold=color_thresh)

    # Save out references to matplotlib handles for different plot elements
    c_matrix.im_ax = im
    c_matrix.ax = im.axes
    c_matrix.fig = im.figure
    c_matrix.text_annots = text_annots

    return c_matrix

# Adapted from
# https://matplotlib.org/3.2.1/gallery/images_contours_and_fields/image_annotated_heatmap.html
def heatmap(data, row_labels, col_labels, ax=None, cmap='binary', clims=None,
            cbar_kw={}, cbarlabel="", display_cbar=True, xlabel="", ylabel="",
            title="", xticks_rotation='horizontal', hide_spine=False,
            grid_color="k", grid_lw=2, **kwargs):
    """
    Create a heatmap from a numpy array and two lists of labels.

    Args:
        data (np.ndarray): A 2D numpy array of shape (N, M).
        row_labels (list[str]): A list or array of length N with the labels for
            the rows. (Y-axis)
        col_labels (list[str]): A list or array of length M with the labels for
            the columns. (X-axis)
        ax (mpl.axes.Axes, optional): A `matplotlib.axes.Axes` instance to which
            the heatmap is plotted.  If not provided, use current axes or create
            a new one.  Optional.
        cmap (str, or mpl.colors.Colormap): str or matplotlib Colormap
            recognized by matplotlib; default='binary'
        clims (Iterable, optional): 2-element iterable mapping to min and max
            values in the colormap; Default of **None** results in min and max
            of ``data`` determining the color limits
        cbar_kw (dict, optional): A dictionary with arguments to
            `matplotlib.Figure.colorbar`.
        cbarlabel (str, optional): The label for the colorbar.
        display_cbar (bool, optional): Whether to display colorbar;
            Default **True**
        xlabel (str, optional): Label for x-axis; Default is ""
        ylabel (str, optional): Label for y-axis; Default is ""
        title (str, optional): Label for plot title; Default is ""
        xticks_rotation (str or float, optional): {'vertical', 'horizontal'} or
            float; default='horizontal'
        hide_spine (bool, optional): Whether to hide spine (outline for plotted
            image); Default is **False**
        grid_color: Any matplotlib compatible color specification for the grid
            drawn between each cell; Default is black
        grid_lw (float, optional): Linewidth for the grid; Default is **2**.
            Note: Value of 1 can look wrong due to 1px alignment issues between
            the grid and the underlying imshow plot
        **kwargs: All other arguments are forwarded to `imshow`.

    Returns:
        im (mpl.image.AxesImage):
        cbar (mpl.colorbar.Colorbar): None if colorbar was disabled

    Example:
        >>> labels = ['civilian', 'military', 'N/A[NaN]']
        >>> data = np.array([[148, 43, 42],[187, 278, 110],[9, 450, 0]])
        >>> # Generate heatmap plot
        >>> im, _ = heatmap(data, row_labels=labels, col_labels=labels,
        >>>     xlabel='Declaration', ylabel='Truth', xticks_rotation=45,
        >>>     display_cbar=False)
        >>> # Annotate heatmap with appropriate font-size
        >>> tex = annotate_heatmap(im)
    """
    assert data.ndim == 2, 'Only 2D ndarray is supported by plot_matrix'
    if not ax:
        fig, ax = plt.subplots()
    else:
        fig = ax.figure

    if clims is None:
        vmin, vmax = None, None
    else:
        vmin, vmax = clims

    # Plot the heatmap
    im = ax.imshow(data, cmap=cmap, interpolation='nearest', vmin=vmin,
                   vmax=vmax, **kwargs)

    # Create colorbar
    cbar = None
    if display_cbar:
        cbar = ax.figure.colorbar(im, ax=ax, **cbar_kw)
        cbar.ax.set_ylabel(cbarlabel, rotation=-90, va="bottom")

    # We want to show all ticks...
    ax.set_xticks(np.arange(data.shape[1]))
    ax.set_yticks(np.arange(data.shape[0]))
    # ... and label them with the respective list entries.
    ax.set_xticklabels(col_labels)
    ax.set_yticklabels(row_labels)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_ylim((data.shape[0] - 0.5, -0.5))
    ax.set_title(title)

    # Let the horizontal axes labeling appear on bottom.
    ax.tick_params(top=False, bottom=True,
                   labeltop=False, labelbottom=True)

    # Rotate the tick labels and set their alignment.
    if xticks_rotation == 'horizontal' or xticks_rotation == 0:
        plt.setp(ax.get_xticklabels(), rotation=xticks_rotation, ha="center",
                 rotation_mode="anchor")
    else:
        plt.setp(ax.get_xticklabels(), rotation=xticks_rotation, ha="right",
                 rotation_mode="anchor")

    # Turn spines off if `hide_spine` == True.
    for edge, spine in ax.spines.items():
        spine.set_visible(not hide_spine)
        spine.set_linewidth(grid_lw)

    # Color grid
    ax.set_xticks(np.arange(data.shape[1]+1)-.5, minor=True)
    ax.set_yticks(np.arange(data.shape[0]+1)-.5, minor=True)
    ax.grid(which="minor", color=grid_color, linestyle='-', linewidth=grid_lw)
    ax.tick_params(which="minor", bottom=False, left=False)

    fig.tight_layout()

    return im, cbar

def annotate_heatmap(im, data=None, valfmt=None, float_precision=2,
                     textcolors=("black", "white"),
                     threshold=None, **textkw):
    """
    A function to annotate a heatmap with values for each pixel. Font-size is
    auto-calculated by default and resize function for the window automatically
    handles rescaling of the font-size

    Parameters
    ----------
    im
        The AxesImage to be labeled.
    data
        Data used to annotate.  If None, the image's data is used.  Optional.
    valfmt (dict)
        The format of the annotations inside the heatmap.  A dict with of format:
        {'int': ..., 'float': ...} where values should either
        use the string format method, e.g. "$ {x:.2f}", or be a
        `matplotlib.ticker.Formatter`.  Optional.
    float_precision (int)
        Overrides default float formatter and only applicable if `valfmt` is
        not provided.
    textcolors
        A pair of colors.  The first is used for values below a threshold,
        the second for those above.  Optional.
    threshold
        Value in data units according to which the colors from textcolors are
        applied.  If None (the default) uses the middle of the colormap as
        separation.  Optional.
    **kwargs
        All other arguments are forwarded to each call to `text` used to create
        the text labels.
    """

    if not isinstance(data, (list, np.ndarray)):
        # returns np.ma (MaskedArray). Set masked values to nan and get
        # underlying data
        data = im.get_array()
        data.set_fill_value(np.nan)
        data = data.data

    int_formatter = mpl.ticker.StrMethodFormatter("{x:0.0f}")
    float_formatter = mpl.ticker.StrMethodFormatter(f"{{x:.{float_precision}f}}")
    DEFAULT_FORMATTER = {'int': int_formatter, 'float': float_formatter}

    # Modify default formatting for integer vs float
    if valfmt is not None:
        # Wrap provided formatter if aprovided as a string specification
        for k, v in valfmt.items():
            if isinstance(v, str):
                valfmt[k] = mpl.ticker.StrMethodFormatter(v)
    else:
        valfmt = {}

    valfmt = {**DEFAULT_FORMATTER, **valfmt}  # Merge formatters with defaults

    # Calculate font-size for annotation and merge with supplied kwargs
    fs = calc_best_fontsize(im)
    text_kwargs = {'fontsize': fs}
    text_kwargs.update(textkw)

    # Normalize the threshold to the images color range.
    if threshold is not None:
        threshold = im.norm(threshold)
    else:
        threshold = im.norm(data.max())/2.

    # Set default alignment to center, but allow it to be
    # overwritten by textkw.
    kw = dict(horizontalalignment="center",
              verticalalignment="center")
    kw.update(text_kwargs)

    # Loop over the data and create a `Text` for each "pixel".
    # Change the text's color depending on the data.
    texts = []
    for i in range(data.shape[0]):
        for j in range(data.shape[1]):
            kw.update(color=textcolors[int(im.norm(data[i, j]) > threshold)])
            c_data = data[i, j]
            # Check if approximately int or not and format accordingly
            if np.isclose(np.mod(c_data, 1), 0, atol=1e-5):
                text = im.axes.text(j, i, valfmt['int'](c_data, None), **kw)
            else:
                text = im.axes.text(j, i, valfmt['float'](c_data, None), **kw)
            texts.append(text)

    def on_resize(event):
        fs = calc_best_fontsize(im)
        for t in texts:
            t.set_fontsize(fs)

    # Bind callback to resize font when window is resized
    cid = im.axes.figure.canvas.mpl_connect('resize_event', on_resize)
    return texts

def calc_best_fontsize(im):
    data = im.get_array()
    nrows, ncols = data.shape
    ax = im.axes
    fig = ax.figure
    w_px, h_px = fig.canvas.get_width_height()
    im_xyxy = ax.get_position().extents  # Normalized
    im_w_px = (im_xyxy[2] - im_xyxy[0]) * w_px
    im_h_px = (im_xyxy[3] - im_xyxy[1]) * h_px

    ratio_num = np.array([im_w_px, im_h_px])

    magic_number = 80
    if (ncols < magic_number) and (nrows < magic_number):
        ratio = min(ratio_num / np.array([nrows, ncols]))
    elif ncols < magic_number:
        ratio = ratio_num[1] / ncols
    elif nrows < magic_number:
        ratio = ratio_num[0] / nrows
    else:
        ratio = 1
    # fs = min(maxFontSize, ceil(ratio / 4)); % the gold formula
    fs = ceil(ratio / 4)
    if fs < 4:  # Font sizes less than 4 still look like crap
        fs = 0
    return fs


# CLASS VIEW2 (mvc for the second activity of the app) task_5
# Authors:
# Miguel Blanco Godón (MiguelBlancoGodon)
# Christian David Outeda García (chrisouteda)

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib
import locale
import gettext
_ = gettext.gettext

class View2:

	# class constructor
	# the view is completely passive
	# we tried to made it the most dependency free that we could
	def __init__(self):

		self.window = Gtk.Window(title='default')
		# TO BE SET FROM OUT

		# VBOX so we can pack more than one item
		vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		self.window.add(vbox)

		# Widgets
		self.interval_name_label = Gtk.Label(label='default')
		vbox.pack_start(self.interval_name_label, expand=False, fill=False, padding=16)
		# Interval tones and hemitones
		self.tones = Gtk.Label('tones')
		vbox.pack_start(self.tones, expand=True, fill=False, padding=0)

		# Gtk Frame for images and names
		hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		vbox.pack_start(hbox, expand=True, fill=False, padding=0)
		vbox.set_spacing(0)

		self.left_frame = Gtk.Frame()
		self.right_frame = Gtk.Frame()

		hbox.pack_start(self.left_frame, expand=False, fill=False, padding=0)
		hbox.pack_start(self.right_frame, expand=False, fill=False, padding=0)
		hbox.set_homogeneous(True)

		# images
		self.left_image = Gtk.Image()

		self.right_image = Gtk.Image()

		labelhbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		vbox.pack_start(labelhbox, expand=True, fill=True, padding=0)
		# label for related songs
		self.related_songs_label = Gtk.Label(label='default')
		# set markup dende model
		labelhbox.pack_start(self.related_songs_label, expand=True, fill=False, padding=8)
		self.favourites = Gtk.Label(label='default')
		# set markup dende model
		labelhbox.pack_start(self.favourites, expand=False, fill=False, padding=75)

		# Songs menu:
		scrolled_window = Gtk.ScrolledWindow()
		vbox.pack_start(scrolled_window, expand=True, fill=True, padding=0)
		self.songsvbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		scrolled_window.add(self.songsvbox)
		scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)

		# Implements related songs from function

	# in order to start any graphical component, we have to start Gtk library
	# this should be the last method called on de view
	def start(self):
		GLib.idle_add(self.window.show_all)

	# various setters

	def set_window_title(self, title):
		self.window.set_title(title)

	def set_window_icon(self, icon_path):
		self.window.set_default_icon_from_file(icon_path)

	def set_tones(self, tones):
		self.tones.set_label(tones)
	def set_interval_name(self, name):
		self.interval_name_label.set_markup("<big>"+_(name)+"</big>")

	def set_left_note(self, note):
		self.left_frame.set_label(note)
		self.left_frame.set_label_align(0.5, 0.5)

	def set_right_note(self, note):
		self.right_frame.set_label(note)
		self.right_frame.set_label_align(0.5, 0.5)

	def set_left_image(self, image_path):
		self.left_image.set_from_file(image_path)
		self.left_frame.add(self.left_image)

	def set_right_image(self, image_path):
		self.right_image.set_from_file(image_path)
		self.right_frame.add(self.right_image)

	def set_songs_separator_label(self, lable):
		self.related_songs_label.set_markup(''+_(lable)+'')

	def set_favourites(self, strfav):
		self.favourites.set_markup(''+_(strfav)+'')

	# For fulfill with songs the songs list
	def fill_with_songs(self, songs_list):
		for (song, link, fav) in songs_list:
			# add a hbox to vbox inside scrolled window
			fhbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
			self.songsvbox.pack_start(fhbox, expand=True, fill=True, padding=0)
			# add a vbox inside hbox to show title and link to the song
			fvbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
			fhbox.pack_start(fvbox, expand=True, fill=False, padding=0)
			# Song title
			songlabel = Gtk.Label(label=song)
			fvbox.pack_start(songlabel, expand=True, fill=True, padding=4)
			# Song link
			songlink = Gtk.LinkButton(uri=link, label=_('Escúchame!'))
			fvbox.pack_start(songlink, expand=True, fill=True, padding=4)
			# Fav
			favlabel = Gtk.Label()
			favlabel.set_markup(''+_(fav)+'')
			fhbox.pack_start(favlabel, expand=False, fill=False, padding=80)

	def mostrar_error(self, error):
		dialog = Gtk.MessageDialog(parent=self, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text=error)
		dialog.run()
		dialog.destroy()


import numpy as np
import matplotlib.pyplot as plt
import scipy.io as sp
from sklearn.linear_model import LogisticRegression
import utils
import copy
from sklearn.datasets import load_iris

def get_choice_stim(path):
    """Given the path to the .mat behavior file,
    return the choice array and the stimulus type array"""
    data = sp.loadmat(path)
    choice = data['data']['response'][0,0]['choice'][0,0][0]
    ntrials = len(choice)
    stim = data['data']['stimuli'][0,0]['loc'][0,0][0][:ntrials]
    return choice, stim

def get_struct_field(data, field, subfield=None):
    if subfield == None:
        return data['data'][field][0,0]
    else:
        return data['data'][field][0,0][subfield][0,0][0]
def get_performance(data):
    """Given the data structure, return the performance in the form:
    An N x 3 array where the columns correspond to
    contrast, performance left, performance right,
    and each column is one contrast"""
    # Load necessary arrays
    choice = data['data']['response'][0,0]['choice'][0,0][0]
    ntrials = len(choice)
    stim = data['data']['stimuli'][0,0]['loc'][0,0][0][:ntrials]
    cons = data['data']['stimuli'][0,0]['opp_contrast'][0,0][0][:ntrials]

    # Process
    curr_con = np.unique(cons)
    output = np.zeros((len(curr_con), 3))
    for i in range(len(curr_con)):
        if np.sum((stim == 2) & (cons == curr_con[i])) == np.sum((stim == 2) & (cons == curr_con[i]) & (choice == 5)):
            perf_l = np.nan
        else:
            perf_l = float(np.sum((stim == 2) & (choice == 2) & (cons == curr_con[i]))) / \
                (np.sum((stim == 2) & (cons == curr_con[i])) - np.sum((stim == 2) & (cons == curr_con[i]) & (choice == 5)))
        #print np.sum((stim == 2) & (choice == 2) & (cons == curr_con[i]))
        if np.sum((stim == 1) & (cons == curr_con[i])) == np.sum((stim == 1) & (cons == curr_con[i]) & (choice == 5)):
            perf_r = np.nan
        else:
            perf_r = float(np.sum((stim == 1) & (choice == 1) & (cons == curr_con[i]))) / \
                (np.sum((stim == 1) & (cons == curr_con[i])) - np.sum((stim == 1) & (cons == curr_con[i]) & (choice == 5)))

        output[i,0] = curr_con[i]
        output[i,1] = perf_l
        output[i,2] = perf_r
    return output

def plot_psychometric(data, color='b', alpha=0.5):
    """Given the data structure, plot the psychometric curve"""
    performance = get_performance(data)
    contrast = get_struct_field(data, 'params', 'contrast')
    condiff = performance[:,0] - contrast
    xaxis = np.hstack([condiff, -np.flip(condiff, axis=0)])
    yaxis = np.hstack([1 - performance[:,2], np.flip(performance[:,1], axis=0)])
    #print(xaxis)
    plt.plot(xaxis, yaxis, color, alpha=alpha)
    plt.xlabel('Contrast difference')
    plt.ylabel('% Left')
    plt.ylim([0, 1])

def get_struct_field(data, field, subfield=None):
    if subfield == None:
        return data['data'][field][0,0]
    else:
        return data['data'][field][0,0][subfield][0,0][0]

class Session(object):
    """A class for a general collection of trials, note that trial order might not be respected"""
    def __init__(self, filename=None, trial_list=None):
        if filename is not None:
            self.filename = filename
            self.data = sp.loadmat(filename)
            self.mouse = get_struct_field(self.data, 'mouse')[0, 0]
            try:
                self.choice = get_struct_field(self.data, 'response', 'choice').astype('int')
            except ValueError:
                print('Warning: no choice field')
                self.choice = None
                raise utils.NoChoiceError('No choice field')

            if trial_list is None:
                trial_list = np.arange(len(self.choice))
                self.ordered = 1  # whether trial order is respected
            else:
                self.ordered = 0
            self.choice = self.choice[trial_list]
            self.ntrials = len(self.choice)
            self.stim = get_struct_field(self.data, 'stimuli', 'loc')[trial_list].astype('int')
            self.simultaneous = self.data['data']['params'][0,0]['simultaneous'][0,0][0,0]
            self.contrast = get_struct_field(self.data, 'params', 'contrast')

            if self.simultaneous:
                self.cons = get_struct_field(self.data, 'stimuli', 'opp_contrast')[trial_list]
            else:
                print('Warning: no opp contrast')
                self.cons = None
                self.contrast = None
                raise utils.NoConsError

            try:
                self.laser = get_struct_field(self.data, 'stimuli', 'laser')[trial_list] - 1
            except ValueError:
                self.laser = None
                print('Warning: no laser')
                #raise utils.NoLaserError('No laser for this session')

    def copy(self):
        """Copy to a new object. Return the copied object"""
        return copy.deepcopy(self)

    def get_choice(self):
        """Returns an array of choice"""
        return self.choice

    def get_stim(self):
        """Returns an array of stimulus location"""
        return self.stim

    def get_opp_contrast(self):
        """Returns an array of opposite contrast"""
        return self.cons

    def get_laser(self):
        """Returns an array of laser on/off condition"""
        return self.laser

    def print_summary(self):
        print(self.filename)
        # Repeated actions
        rewarded = np.where(self.choice == self.stim)[0]
        errors = np.where((self.choice != self.stim) & (self.choice != 5))[0]
        if rewarded[-1] == len(self.choice) - 1:
            rewarded_p1 = rewarded[:-1] + 1
            rewarded = rewarded[:-1]
        else:
            rewarded_p1 = rewarded + 1

        if errors[-1] == len(self.choice) - 1:
            errors_p1 = errors[:-1] + 1
            errors = errors[:-1]
        else:
            errors_p1 = errors + 1

        choice_t1 = self.choice[rewarded_p1]
        choice_t = self.choice[rewarded][choice_t1 != 5]
        stim_t1 = self.stim[rewarded_p1][choice_t1 != 5]
        choice_t1 = choice_t1[choice_t1 != 5]
        assert(len(choice_t) == len(stim_t1) and len(choice_t) == len(choice_t1))

        choice_error_t1 = self.choice[errors_p1]
        choice_error_t = self.choice[errors][choice_error_t1 != 5]
        stim_err_t1 = self.stim[errors_p1][choice_error_t1 != 5]
        choice_error_t1 = choice_error_t1[choice_error_t1 != 5]
        assert(len(choice_error_t1) == len(choice_error_t) and len(choice_error_t1) == len(stim_err_t1))

        frac_rep = np.sum(choice_t == choice_t1) / len(choice_t)
        frac_rep_err = np.sum(choice_error_t == choice_error_t1) / len(choice_error_t)
        frac_corr = np.sum(choice_t1 == stim_t1) / len(choice_t)
        frac_corr_err = np.sum(choice_error_t1 == stim_err_t1) / len(choice_error_t1)
        print('    - Repeated fraction after correct: %.2f' % frac_rep)
        print('    - Repeated fraction after error: %.2f' % frac_corr_err)

        return frac_rep, frac_rep_err, frac_corr, frac_corr_err

    def get_performance(self):
        """Given the data structure, return the performance in the form:
        An N x 3 array where the columns correspond to
        contrast, performance left, performance right,
        and each column is one contrast"""
        # Load necessary arrays
        choice = self.get_choice()
        stim = self.get_stim()
        cons = self.get_opp_contrast()

        # Process
        curr_con = np.unique(cons)
        output = np.zeros((len(curr_con), 3))
        for i in range(len(curr_con)):
            if np.sum((stim == 2) & (cons == curr_con[i])) == np.sum((stim == 2) & (cons == curr_con[i]) & (choice == 5)):
                perf_l = np.nan
            else:
                perf_l = float(np.sum((stim == 2) & (choice == 2) & (cons == curr_con[i]))) / \
                    (np.sum((stim == 2) & (cons == curr_con[i])) - np.sum((stim == 2) & (cons == curr_con[i]) & (choice == 5)))
            #print np.sum((stim == 2) & (choice == 2) & (cons == curr_con[i]))
            if np.sum((stim == 1) & (cons == curr_con[i])) == np.sum((stim == 1) & (cons == curr_con[i]) & (choice == 5)):
                perf_r = np.nan
            else:
                perf_r = float(np.sum((stim == 1) & (choice == 1) & (cons == curr_con[i]))) / \
                    (np.sum((stim == 1) & (cons == curr_con[i])) - np.sum((stim == 1) & (cons == curr_con[i]) & (choice == 5)))

            output[i,0] = curr_con[i]
            output[i,1] = perf_l
            output[i,2] = perf_r
        return output

    def plot_raw_performance(self):
        """Plot a visualization of raw performance"""
        choice = self.get_choice()
        ntrials = self.ntrials
        stim = self.get_stim()
        trialstart = get_struct_field(self.data, 'response', 'trialstart') / 60

        # Plot the time-outs
        timeouts = np.where(choice == 5)
        plt.figure(figsize=(20, 10))
        plt.plot(np.arange(self.ntrials)[timeouts], stim[timeouts], 'ko')

        # Plot correct/incorrect trials
        corr = np.where((choice != 5) & (stim == choice))[0]
        incorr = np.where((choice != 5) & (stim != choice))[0]
        plt.plot(np.arange(self.ntrials)[corr], stim[corr], 'bo')
        plt.plot(np.arange(self.ntrials)[incorr], stim[incorr], 'ro')

        plt.xlabel('Time (min)')
        plt.yticks([1, 2], ['Left', 'Right'])

    def plot_cumulative_bias(self, n):
        """Cumulative bias of last n trials"""
        choice = self.get_choice()
        ntrials = self.ntrials
        stim = self.get_stim()
        trialstart = get_struct_field(self.data, 'response', 'trialstart') / 60

        # Plot the time-outs
        timeouts = np.where(choice == 5)
        plt.figure(figsize=(20, 10))
        plt.plot(np.arange(self.ntrials)[timeouts], stim[timeouts], 'ko')

        # Plot correct/incorrect trials
        corr = (choice != 5) & (stim == choice)
        incorr = (choice != 5) & (stim != choice)
        ncorr_cum = np.convolve(corr, np.ones(n), mode='valid')
        nincorr_cum = np.convolve(incorr, np.ones(n), mode='valid')

        perf_cum = ncorr_cum / (ncorr_cum + nincorr_cum)
        plt.plot(perf_cum)

        return perf_cum

    def plot_psychometric(self, color='b', alpha=0.5):
        """Given the data structure, plot the psychometric curve"""
        performance = self.get_performance()
        contrast = self.contrast
        condiff = performance[:,0] - contrast
        xaxis = np.hstack([condiff, -np.flip(condiff, axis=0)])
        yaxis = np.hstack([1 - performance[:,2], np.flip(performance[:,1], axis=0)])
        #print(xaxis)
        plt.plot(xaxis, yaxis, color=color, alpha=alpha)
        plt.xlabel('Contrast difference')
        plt.ylabel('% Left')
        plt.ylim([0, 1])

    def find_logistic_coef(self):
        """Returns the logistic regression of the session"""
        choice = self.get_choice() - 1
        stim = self.get_stim() - 1

        # Make a design matrix
        currchoice = choice[1:]
        currstim = stim[1:]
        prevchoice = choice[:-1]
        prevstim = stim[:-1]
        prev_reward = prevchoice == prevstim

        y_b = currchoice[(currchoice != 4) & (prevchoice != 4)]
        y_b = y_b * 2 - 1
        print(y_b)

        X = np.vstack([currstim * 2 - 1]).T
        X_b = X[(currchoice != 4) & (prevchoice != 4),:]
        #print(X_b)

        if len(np.unique(y_b)) < 2:
            raise ValueError('Only one value in y')
        # Do logistic regression
        clf = LogisticRegression(C=1e10).fit(X_b, y_b)

        # Print the coefficients
        return np.hstack([clf.intercept_, clf.coef_.flatten()])

    def make_subsession(self, trial_lst):
        """
        For making a subsession, given the list of trials
        :param trial_lst: a list of trials
        :return: a Supersession object corresponding to the indicated trials
        """
        if self.filename is not None:
            subsession = Session(self.filename, trial_lst)
        else:
            subsession = self.copy()
            subsession.ntrials = len(trial_lst)
            subsession.choice = self.choice[trial_lst]
            subsession.stim = self.stim[trial_lst]
            subsession.cons = self.cons[trial_lst]
            subsession.laser = self.laser[trial_lst]
        return subsession

class SessionGroup(object):
    """A class for groups of sessions"""
    def __init__(self, sess_lst):
        """Initialize a group of sessions"""
        self.sess_lst = sess_lst
        self.nsess = len(sess_lst)

    def as_session(self):
        """
        Make into a Session object
        :return:
        """
        return combine_multiple_sessions(self.sess_lst)

    def plot_agg_performance(self):
        plt.figure()
        combined = combine_multiple_sessions(self.sess_lst)
        combined.plot_psychometric()

    def plot_individual_psychometric(self):
        nx = np.ceil(np.sqrt(self.nsess))
        ny = np.ceil(self.nsess / nx)
        for i in range(self.nsess):
            plt.subplot(nx, ny, i + 1)
            self.sess_lst[i].plot_psychometric()

    def plot_individual_raw_performance(self):
        T0 = 0
        plt.figure(figsize=(20, 10))
        for sess in self.sess_lst:
            choice = sess.get_choice()
            stim = sess.get_stim()
            trialstart = get_struct_field(sess.data, 'response', 'trialstart') / 60
            trialstart += T0
            T0 = trialstart[-1]
            assert(len(trialstart) == len(choice))

            # Plot the time-outs
            timeouts = np.where(choice == 5)
            plt.plot(trialstart[timeouts], stim[timeouts], 'ko')

            # Plot correct/incorrect trials
            corr = np.where((choice != 5) & (stim == choice))[0]
            incorr = np.where((choice != 5) & (stim != choice))[0]
            plt.plot(trialstart[corr], stim[corr], 'bo')
            plt.plot(trialstart[incorr], stim[incorr], 'ro')

            plt.xlabel('Time (min)')
            plt.yticks([1, 2], ['Left', 'Right'])
            plt.vlines(T0, -1, 3)

def concat_safe(arr1, arr2):
    """
    Concatenate, but handle cases of None
    :param arr1:
    :param arr2:
    :return:
    """
    if arr1 is not None and arr2 is not None:
        combined = np.concatenate((arr1, arr2))
    else:
        print('Warning: during combine sessions, encountered none in choice')
        if arr1 is None:
            combined = arr2
        else:
            combined = arr1
    return combined

# For combining sessions
def combine_sessions(sess1, sess2):
    """
    Combine two sessions
    :param sess1: a Session instance
    :param sess2: a Session instance
    :return: a Session object
    """
    sess1copy = sess1.copy()
    sess1copy.ntrials = sess1.ntrials + sess2.ntrials

    sess1copy.choice = concat_safe(sess1.choice, sess2.choice)
    sess1copy.stim = concat_safe(sess1.stim, sess2.stim)
    sess1copy.cons = concat_safe(sess1.cons, sess2.cons)
    sess1copy.laser = concat_safe(sess1.laser, sess2.laser)
    sess1copy.ordered = 0
    sess1copy.filename = None
    return sess1copy

def combine_multiple_sessions(session_list):
    """
    for combining multiple sessions given by a list
    :param session_list: a list of Session instances
    :return: a Session object
    """
    if len(session_list) == 1:
        return session_list[0]
    else:
        sess1copy = session_list[0].copy()
        ntrials = sess1copy.ntrials
        for id, session in enumerate(session_list[1:]):
            ntrials += session.ntrials
            sess1copy = combine_sessions(sess1copy, session)
    assert len(sess1copy.choice) == ntrials
    assert len(sess1copy.stim) == ntrials
    assert len(sess1copy.cons) == ntrials
    #assert len(sess1copy.laser) == ntrials
    return sess1copy


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
os.chdir('D:\\DATA SCIENCE\\MACHINE LEARNING\\Simple Linear Regression\\')
os.getcwd()
df1= pd.read_csv('Salary_Data.csv')
df1
#x independent variable
x= df1.iloc[:,:-1].values
x
#y dependent variables
y= df1.iloc[:,1].values
y
#creating a plot to check the trend
plt.plot(x,y)
plt.show()
#train test split
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test= train_test_split(x,y, test_size=0.2)
print('x shape',x.shape)
print('y shape',y.shape)
print('x_train shape',x_train.shape)
print('x_test shape',x_test.shape)
print('y_train shape',y_train.shape)
print('y_test shape',y_test.shape)
#model fitting

from sklearn.linear_model import LinearRegression
lr= LinearRegression()
lr.fit(x_train, y_train)
print(lr)
#prediction y=mx+c y=prediction m=coefficient x=experience c = intercept

y_pred = lr.predict(x_test)
print(y_pred)

#y= mx +c (Coefficient and Interceptor Values)
#Y= slope
print ('Coefficient', lr.coef_)
print ('Intercept', lr.intercept_)

#display the prediction with difference

df_x_test = pd.DataFrame(x_test, columns=['Experience'])
df_y_test = pd.DataFrame(y_test, columns=['Salary'])
df_y_test_pred =pd.DataFrame(y_pred, columns=['Prediction'])
df_diff = df_y_test-df_y_test_pred
y_test_pred = pd.concat([df_x_test ,df_y_test, df_y_test_pred ],axis =1)
y_test_pred['Difference']= df_y_test ['Salary']- df_y_test_pred['Prediction']
print(y_test_pred)

#accuracy of model
from sklearn.metrics import r2_score
accuracy = r2_score(y_test,y_pred)
print(accuracy)

#test data
plt.scatter(x_test,y_test)
plt.plot(x_test,y_pred,'r')
plt.show()

#now prediction of complete data
y_pred_final = lr.predict(x)
print(y_pred_final)

#final result in dataframe storing in excel

y_pred_final = pd.DataFrame(y_pred_final,columns=['Prediction'])
result=pd.concat([df1,y_pred_final],axis=1)
result['Difference']=result['Salary']-result['Prediction']
print(result)
result.to_excel('D:\\DATA SCIENCE\\MACHINE LEARNING\\Simple Linear Regression\\prediction.xlsx')

from sklearn.metrics import r2_score
accuracy=r2_score(y,y_pred_final)
print(accuracy)

plt.plot(x,y)
plt.plot(x,y_pred_final,'red')
plt.show()


import pickle  # 对象序列化
import zlib  # 压缩数据
from datetime import datetime, timedelta  # 设置缓存超时间隔
from pymongo import MongoClient
from bson.binary import Binary  # MongoDB存储二进制的类型

class MongoCache(object):
    '''
    数据库缓存
    '''

    # # 连接数据库
    # client = pymongo.MongoClient(host='localhost', port=27017)
    # # 获取数据库实例
    # db = client.test
    # # 创建studnets集合
    # collection = db.students

    # result = requests.get('https://www.qiushibaike.com/8hr/page/1')

    def __init__(self, client=None, expires=timedelta(days=30)):
        self.client = MongoClient("localhost", 27017),
        self.db = self.client.cache,
        web_page = self.db.webpage,
        self.db.webpage.create_index('timestamp', expireAfterSeconds=expires.total_seconds())

    def __setitem__(self, key, value):
        record = {"result": Binary(zlib.compress(pickle.dumps(value))), "timestamp": datetime.utcnow()}
        self.db.webpage.update({"_id": key}, {"$set": record}, upsert=True)

    def __getitem__(self, item):
        '''
        将缓存数据照item作为key取出（key任然是下载的url）
        :param self:
        :param item:
        :return:
        '''
        record = self.db.webpage.find_one({"_id": item})
        if record:
            return pickle.loads(zlib.decompress(record['result']))
        else:
            raise KeyError(item + "does not exist")

    def __contains__(self, item):
        '''
        当调用in,not in 会调用该方法判断链接对应网址是否在数据库中
        :param self:
        :param item:        下载的url链接
        :return:
        '''
        try:
            self[item]
        except KeyError:
            return False
        else:
            return True

    def clear(self):
        self.db.webpage.drop()


from math import atan2
from collections import defaultdict

def sights(point, lines):
    si, sj = point
    direct = set()
    for i in range(len(lines)):
        for j in range(len(lines[i])):
            if (i, j) != (si, sj) and lines[i][j] == '#':
                direct.add(atan2(i-si, j-sj))
    return len(direct)

def solve(lines):
    result = 0
    for i in range(len(lines)):
        for j in range(len(lines[i])):
            if lines[i][j] == '#':
                asteroids = sights((i,j), lines)
                if result < asteroids:
                    result = asteroids
                    pos = (i, j)
    return result, pos

def manhattan(p1, p2):
    return abs(p1[0] - p2[0]) + abs(p1[1] - p2[1])

def position(point, pos):
    y, x = point
    sy, sx = pos
    l = atan2(y-sy, x-sx)
    if y < sy and x >= sx:
        return ('q1', l)
    elif y >= sy and x >= sx:
        return ('q2', l)
    elif y >= sy and x < sx:
        return ('q3', l)
    elif y < sy and x <= sx:
        return ('q4', l)

def rotate(pos, lines):
    si, sj = pos
    seen = set([pos])
    count = 0
    total = sum([x.count('#') for x in lines]) - 1
    while count < min(total, 200):
        halves = defaultdict(dict)
        for i in range(len(lines)):
            for j in range(len(lines[i])):
                point = (i, j)
                if lines[i][j] == '#' and point not in seen:
                    m = manhattan(point, pos)
                    save = (m, point)
                    key, l = position(point, pos)
                    halves[key][l] = min(save, halves[key].get(l, save))

        for q in ['q1', 'q2', 'q3', 'q4']:
            h = sorted(halves[q].items())
            for _, x in h: seen.add(x[1])
            count += len(h)
            if count > 200:
                count -= len(h)
                for _, x in h:
                    count += 1
                    if count == 200:
                        return x[1]
    return None

if __name__ == '__main__':
    # sol 1
    lines = []
    with open('inputs/10-input') as f:
        for line in f:
            lines.append(line.strip())

    result, pos = solve(lines)
    print(result)
    print(pos)

    # sol 2
    ry, rx = rotate(pos, lines)
    print(rx*100 + ry)


"""Organization forms."""
from django import forms
from django.contrib.auth.models import User
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from django.core.validators import EmailValidator
from django.db.models import Q
from django.utils.translation import gettext_lazy as _

from readthedocs.core.history import SimpleHistoryModelForm
from readthedocs.core.permissions import AdminPermission
from readthedocs.core.utils import slugify
from readthedocs.core.utils.extend import SettingsOverrideObject
from readthedocs.invitations.models import Invitation
from readthedocs.organizations.constants import ADMIN_ACCESS, READ_ONLY_ACCESS
from readthedocs.organizations.models import (
    Organization,
    OrganizationOwner,
    Team,
    TeamMember,
)

class OrganizationForm(SimpleHistoryModelForm):

    """
    Base organization form.

    :param user: User instance, responsible for ownership of Organization
    :type user: django.contrib.auth.models.User
    """

    # We use the organization slug + project name
    # to form the final project slug.
    # A valid project slug is 63 chars long.
    name = forms.CharField(max_length=32)

    class Meta:
        model = Organization
        fields = ["name", "email", "description", "url"]
        labels = {
            "name": _("Organization Name"),
            "email": _("Billing Email"),
        }

    # Don't use a URLField as a widget, the validation is too strict on FF
    url = forms.URLField(
        widget=forms.TextInput(attrs={"placeholder": "http://"}),
        label=_("Site URL"),
        required=False,
    )

    def __init__(self, *args, **kwargs):
        try:
            self.user = kwargs.pop("user")
        except KeyError:
            raise TypeError(
                "OrganizationForm expects a `user` keyword argument",
            )
        super().__init__(*args, **kwargs)

    def clean_name(self):
        """Raise exception on duplicate organization slug."""
        name = self.cleaned_data["name"]

        # Skip slug validation on already created organizations.
        if self.instance.pk:
            return name

        potential_slug = slugify(name)
        if not potential_slug:
            raise forms.ValidationError(
                _("Invalid organization name: no slug generated")
            )
        if Organization.objects.filter(slug=potential_slug).exists():
            raise forms.ValidationError(
                _("Organization %(name)s already exists"),
                params={"name": name},
            )
        return name

class OrganizationSignupFormBase(OrganizationForm):

    """
    Simple organization creation form.

    This trims down the number of inputs required to create a new organization.
    This is used on the initial organization signup, to keep signup terse.

    :param user: User instance, responsible for ownership of Organization
    :type user: django.contrib.auth.models.User
    """

    class Meta:
        model = Organization
        fields = ["name", "email"]
        labels = {
            "name": _("Organization Name"),
            "email": _("Billing Email"),
        }

    url = None

    @staticmethod
    def _create_default_teams(organization):
        organization.teams.create(name="Admins", access=ADMIN_ACCESS)
        organization.teams.create(name="Read Only", access=READ_ONLY_ACCESS)

    def save(self, commit=True):
        org = super().save(commit)

        # If not committing, we can't save M2M fields
        if not commit:
            return org

        # Add default teams
        OrganizationOwner.objects.create(
            owner=self.user,
            organization=org,
        )
        self._create_default_teams(org)
        return org

class OrganizationSignupForm(SettingsOverrideObject):
    _default_class = OrganizationSignupFormBase

class OrganizationOwnerForm(forms.Form):

    """Form to manage owners of the organization."""

    username_or_email = forms.CharField(label=_("Email address or username"))

    def __init__(self, *args, **kwargs):
        self.organization = kwargs.pop("organization", None)
        self.request = kwargs.pop("request", None)
        super().__init__(*args, **kwargs)

    def clean_username_or_email(self):
        """Lookup owner by username or email, detect collisions with existing owners."""
        username = self.cleaned_data["username_or_email"]
        user = User.objects.filter(
            Q(username=username)
            | Q(emailaddress__verified=True, emailaddress__email=username)
        ).first()
        if user is None:
            raise forms.ValidationError(
                _("User %(username)s does not exist"),
                params={"username": username},
            )
        if self.organization.owners.filter(pk=user.pk).exists():
            raise forms.ValidationError(
                _("User %(username)s is already an owner"),
                params={"username": username},
            )
        return user

    def save(self):
        invitation, _ = Invitation.objects.invite(
            from_user=self.request.user,
            to_user=self.cleaned_data["username_or_email"],
            obj=self.organization,
            request=self.request,
        )
        return invitation

class OrganizationTeamBasicFormBase(SimpleHistoryModelForm):

    """Form to manage teams."""

    class Meta:
        model = Team
        fields = ["name", "access", "organization"]
        error_messages = {
            NON_FIELD_ERRORS: {
                "unique_together": _("Team already exists"),
            },
        }

    organization = forms.CharField(widget=forms.HiddenInput(), required=False)

    def __init__(self, *args, **kwargs):
        self.organization = kwargs.pop("organization", None)
        super().__init__(*args, **kwargs)

    def clean_organization(self):
        """Hard code organization return on form."""
        return self.organization

class OrganizationTeamBasicForm(SettingsOverrideObject):
    _default_class = OrganizationTeamBasicFormBase

class OrganizationTeamProjectForm(forms.ModelForm):

    """Form to manage access of teams to projects."""

    class Meta:
        model = Team
        fields = ["projects"]

    def __init__(self, *args, **kwargs):
        self.organization = kwargs.pop("organization", None)
        super().__init__(*args, **kwargs)
        self.fields["projects"] = forms.ModelMultipleChoiceField(
            queryset=self.organization.projects,
            widget=forms.CheckboxSelectMultiple,
        )

class OrganizationTeamMemberForm(forms.Form):

    """Form to manage all members of the organization."""

    username_or_email = forms.CharField(label=_("Email address or username"))

    def __init__(self, *args, **kwargs):
        self.team = kwargs.pop("team", None)
        self.request = kwargs.pop("request", None)
        super().__init__(*args, **kwargs)

    def clean_username_or_email(self):
        """
        Validate the user to invite to.

        We search for an existing user by username or email,
        if none is found, we try to validate if the input is an email,
        in that case we send an invitation to that email.
        """
        username = self.cleaned_data["username_or_email"]
        user = User.objects.filter(
            Q(username=username)
            | Q(emailaddress__verified=True, emailaddress__email=username)
        ).first()

        if user:
            return self.validate_member_user(user)

        # If it's a valid email,
        # then try sending an invitation to it.
        try:
            validator = EmailValidator(code="lookup not an email")
            validator(username)
            return username
        except ValidationError as error:
            if error.code != "lookup not an email":
                raise

        raise forms.ValidationError(
            _("User %(username)s does not exist"), params={"username": username}
        )

    def validate_member_user(self, member):
        """Verify duplicate team member doesn't already exists."""
        if TeamMember.objects.filter(team=self.team, member=member).exists():
            raise forms.ValidationError(
                _("User is already a team member"),
            )
        return member

    def save(self):
        """Create an invitation only if the user isn't already a member."""
        user = self.cleaned_data["username_or_email"]
        if isinstance(user, User):
            # If the user is already a member or the organization
            # don't create an invitation.
            if (
                AdminPermission.members(self.team.organization)
                .filter(pk=user.pk)
                .exists()
            ):
                member = self.team.organization.add_member(user, self.team)
                if user != self.request.user:
                    member.send_add_notification(self.request)
                return user
            invitation, _ = Invitation.objects.invite(
                from_user=self.request.user,
                to_user=user,
                obj=self.team,
                request=self.request,
            )
            return invitation
        invitation, _ = Invitation.objects.invite(
            from_user=self.request.user,
            to_email=user,
            obj=self.team,
            request=self.request,
        )
        return invitation


from gluon import current
from gluon.storage import Storage
from common_utils import D, DQ, remove_fractions
from datetime import date, timedelta, datetime

def new(id_store, now, user):
    db = current.db

    return db.purchase.insert(
        id_store=id_store,
        created_on=now,
        modified_on=now,
        created_by=user.id
    )

def postprocess_stock_item(stock_item):
    stock_item.serial_numbers = stock_item.serial_numbers.replace('_', ',') if stock_item.serial_numbers else None
    # primitive serial number count verification
    # if stock_item.serial_numbers:
    #     stock_items_count = stock_item.serial_numbers.split(',')
    #     if not stock_items_count[-1]:
    #         stock_items_count.pop()
    #     print len(stock_items_count)
    # else:
    #     stock_item.serial_numbers = None

    # recalculate the taxes.
    total_tax = 1 if stock_item.id_item.taxes else 0
    for tax in stock_item.id_item.taxes or []:
        total_tax *= tax.percentage / 100.0
    stock_item.taxes = D(stock_item.price or 0) * D(total_tax)
    if not stock_item.id_item.allow_fractions:
        stock_item.purchase_qty = DQ(remove_fractions(stock_item.purchase_qty))
    return stock_item

def commit(purchase):
    """ Given a purchase Row, generates stock for every stock item in associated with the purchase """

    db = current.db

    # generate stocks for every purchase item
    stock_items = db(db.stock_item.id_purchase == purchase.id).iterselect()
    for stock_item in stock_items:
        stock_item = postprocess_stock_item(stock_item)
        stock_item.stock_qty = stock_item.purchase_qty
        stock_item.id_store = purchase.id_store.id
        # set the stock quantity to the purchased quantity
        stock_item.update_record()
        # update the item prices
        item = db.item(stock_item.id_item)
        # base price should not be 0
        if stock_item.base_price > 0:
            item.base_price = stock_item.base_price
            item.price2 = stock_item.price2
            item.price3 = stock_item.price3
        item.update_record()
    purchase.is_done = True
    purchase.update_record()

    # in this case purchase is being paid with creadit so we need to create an account payable
    if purchase.id_payment_opt.credit_days > 0:
        now = purchase.modified_on
        epd = date(now.year, now.month, now.day)
        epd += timedelta(days=purchase.id_payment_opt.credit_days)
        db.account_payable.insert(id_purchase=purchase.id, epd=epd)

    return purchase


import logging, io, pickle, pathlib, requests, json, emoji
import os.path
import flask
import telegram.constants
import threading
from urllib.request import urlopen
from flask import Flask, request
from flask_cors import CORS
from telegram import    Update,\
                        InlineKeyboardButton,\
                        InlineKeyboardMarkup,\
                        ReplyKeyboardMarkup,\
                        ReplyKeyboardRemove,\
                        MessageEntity,\
                        error
from telegram.ext import    filters,\
                            MessageHandler,\
                            ApplicationBuilder,\
                            CommandHandler,\
                            ContextTypes,\
                            ConversationHandler,\
                            CallbackQueryHandler,\
                            TypeHandler
from telegram.ext.filters import MessageFilter
from PIL import Image
import sys

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)

if pathlib.Path('stickerpacks').exists():
    with open('stickerpacks', 'rb') as packs:
        stickerpacks = pickle.load(packs)
else:
    with open('stickerpacks', 'wb') as packs:
        stickerpacks = dict()
        pickle.dump(stickerpacks, packs)

if pathlib.Path('IDs').exists():
    with open('IDs', 'rb') as file:
        IDs = pickle.load(file)
else:
    IDs = False

ACTIONS = {"thumbnail" : "Change thumbnail",
           "delete sticker" : "Delete sticker",
           "add sticker" : "Add sticker",
           "title" : "Change title",
           "emoji": "Change sticker emoji list"}

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    global IDs
    if IDs == False:
        with open('IDs', 'wb') as file:
            IDs = { "user_id": update.effective_user.id,
                    "chat_id": update.effective_chat.id}
            pickle.dump(IDs, file)
    keyboard = []
    if stickerpacks:
        for packname in stickerpacks.keys():
            keyboard.append([InlineKeyboardButton(f"{packname}", callback_data = packname)])
    keyboard.append([InlineKeyboardButton("Create new", callback_data = 'makenew')])
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.message.reply_text("Please choose:", reply_markup = reply_markup)
    return 'pack_select'

def build_keyboard(actions, columns = 2, row_character_limit = 35):
    row_length = 0
    keyboard = [[]]
    for action in actions:
        keyboard[-1].append(action)
        row_length += 1
        if row_length >= columns:
            row_length = 0
            keyboard.append([])
    return keyboard

async def pack_select(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    stickerpack_data = query.data
    if stickerpack_data == 'makenew':
        await context.bot.send_message( chat_id = update.effective_chat.id,
                                        text = "Send title for new sticker pack",
                                        reply_markup=ReplyKeyboardRemove())
        return 'newpack'
    else:
        return await get_pack(update, context, stickerpack_data)

async def get_pack(update: Update, context: ContextTypes.DEFAULT_TYPE, stickerpack_data):
    try:
        await context.bot.get_sticker_set(name = stickerpacks[stickerpack_data])
    except error.BadRequest:
        with open('stickerpacks', 'wb') as packs:
            del stickerpacks[stickerpack_data]
            pickle.dump(stickerpacks, packs)
        await context.bot.send_message(chat_id = update.effective_chat.id,
                                        text = f"Pack {stickerpack_data} does not exist!")
        return ConversationHandler.END
    if stickerpack_data:
        reply_keyboard = build_keyboard(ACTIONS.values())
        markup = ReplyKeyboardMarkup(keyboard = reply_keyboard,
                                     one_time_keyboard = True,
                                     input_field_placeholder = "I live in your walls",
                                     resize_keyboard = True)
        await context.bot.send_message( chat_id = update.effective_chat.id,
                                        text = f"You chose {stickerpack_data}, now pick an action",
                                        reply_markup = markup)
        context.user_data["current_pack_title"] = stickerpack_data
        context.user_data["current_pack_name"] = stickerpacks[stickerpack_data]
        return "processing"
    else:
        await context.bot.send_message(chat_id=update.effective_chat.id,
                                       text="No stickerpack data!")
        return ConversationHandler.END

async def newsticker(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Send new sticker as an attachment/7tv link/direct link",
                                    reply_markup = ReplyKeyboardRemove())
    return 'new_sticker_methods'

async def add_sticker(image: bytes, update, context: ContextTypes.DEFAULT_TYPE) -> bool:
    #process the image and add to sticker set
    image = Image.open(image)
    image = image.crop(image.getbbox())  # trim transparent pixels
    width, height = image.size
    if width > height:
        ratio = height / width
        width = 512
        height = int(512 * ratio)
    elif width < height:
        ratio = width / height
        height = 512
        width = int(512 * ratio)
    else:
        width = height = 512
    image = image.resize((width, height))
    image_io = io.BytesIO()
    image.save(image_io, "PNG")
    input_sticker = telegram.InputSticker(sticker=image_io.getvalue(),
                                          emoji_list=["ðŸ˜¢", "ðŸ˜¥"])
    successful = True
    user_id = update.effective_user.id
    current_pack_name = context.user_data["current_pack_name"] #pack name
    insertion = await context.bot.add_sticker_to_set(user_id=user_id,
                                                     name=current_pack_name,
                                                     sticker=input_sticker)
    if insertion is successful:
        current_stickerset = await context.bot.get_sticker_set(current_pack_name)
        sticker_id = current_stickerset.stickers[-1].file_id
        context.user_data['sticker_id'] = sticker_id
        await context.bot.send_sticker(chat_id=update.effective_chat.id,
                                       sticker=sticker_id)
        return True
    else:
        return False

async def new_sticker_from_link(update: Update, context: ContextTypes.DEFAULT_TYPE):
    link = update.message.text
    image = requests.get(link).content
    image = io.BytesIO(image)
    successful = await add_sticker(image = image, update = update, context = context)
    if successful:
        await update.message.reply_text("Added, send new emoji for sticker, 1-20")
    else:
        await update.message.reply_text("error")
    return 'change_emoji'

async def new_sticker_from_7tv(update: Update, context: ContextTypes.DEFAULT_TYPE):
    link = update.message.text
    source_id = link[link.rfind('/'):]  # get source id of emote from link
    response = requests.get(f"https://7tv.io/v3/emotes/{source_id}")  # get request from api
    emote_info = json.loads(response.content.decode('utf-8'))  # load json from request
    if emote_info["animated"]:
        await update.message.reply_text("image non-static")
        return ConversationHandler.END
    image_url = f"https://cdn.7tv.app/emote/{source_id}/4x.png"
    image = requests.get(image_url).content
    image = io.BytesIO(image)
    successful = await add_sticker(image = image, update = update, context = context)
    if successful:
        await update.message.reply_text("Added, send new emoji for sticker, 1-20")
    else:
        await update.message.reply_text("error")
    return 'change_emoji'

async def sticker_for_emoji(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Send a sticker that you wanna set associated emoji list for",
                                    reply_markup = ReplyKeyboardRemove())
    return 'emoji_for_sticker'

async def emoji_for_sticker(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data['sticker_id'] = update.message.sticker.file_id
    await update.message.reply_text("Send new emoji for sticker, 1-20")
    return 'change_emoji'

async def change_emoji(update: Update, context: ContextTypes.DEFAULT_TYPE):
    emojis = emoji.distinct_emoji_list(update.message.text)
    await context.bot.set_sticker_emoji_list(sticker = context.user_data['sticker_id'],
                                             emoji_list = emojis)
    await update.message.reply_text("epic")
    return ConversationHandler.END

async def new_sticker_attachment(update: Update, context: ContextTypes.DEFAULT_TYPE):
    photo_id = update.message.document.file_id
    image_data = await context.bot.get_file(photo_id)
    image = io.BytesIO()
    await image_data.download_to_memory(image)
    successful = await add_sticker(image = image, update = update, context = context)
    if successful:
        await update.message.reply_text("Added, send new emoji for sticker, 1-20")

    else:
        await update.message.reply_text("error")
    return 'change_emoji'

async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("bye",
                                    reply_markup = ReplyKeyboardRemove())
    return ConversationHandler.END

async def unknown(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await context.bot.send_message( chat_id=update.effective_chat.id,
                                    text="Sorry, I didn't understand that command.",
                                    reply_markup = ReplyKeyboardRemove())

async def newpack(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user_id = update.effective_user.id
    bot_name = context.bot.username
    pack_title = update.message.text
    nospace_title = '_'.join(pack_title.split())
    pack_name = f'{nospace_title}_by_{bot_name}'
    sticker_format = telegram.constants.StickerFormat.STATIC
    default_sticker = open("saj512.png", "rb")
    default_input_sticker = telegram.InputSticker(sticker = default_sticker,
                                                  emoji_list = ["ðŸ˜¢", "ðŸ˜¥"])
    successful = True
    creation = await context.bot.create_new_sticker_set(user_id = user_id,
                                                        name = pack_name,
                                                        title = pack_title,
                                                        stickers = [default_input_sticker],
                                                        sticker_format = sticker_format)
    if creation is successful:
        with open('stickerpacks', 'wb') as packs:
            stickerpacks[pack_title] = pack_name
            pickle.dump(stickerpacks, packs)
        await update.message.reply_text(f"Pack {pack_title} created")
        return 'processing'
    else:
        await update.message.reply_text("Error")

    return ConversationHandler.END

async def new_pack_title(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Send new sticker pack title",
                                    reply_markup = ReplyKeyboardRemove())
    return 'set_new_pack_title'

async def delete_sticker(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Send a sticker that you want to delete",
                                    reply_markup = ReplyKeyboardRemove())
    return 'pick_delete_sticker'

async def pick_delete_sticker(update: Update, context: ContextTypes.DEFAULT_TYPE):
    sticker_id = update.message.sticker.file_id
    deletion = await context.bot.delete_sticker_from_set(sticker_id)
    successful = True
    if deletion is successful:
        await update.message.reply_text("Deleted")
    else:
        await update.message.reply_text("Error")
    return ConversationHandler.END

async def set_new_pack_title(update: Update, context: ContextTypes.DEFAULT_TYPE):
    current_pack_name = context.user_data["current_pack_name"]
    new_title = update.message.text
    title_change = await context.bot.set_sticker_set_title( name = current_pack_name,
                                                            title = new_title)
    successful = True
    if title_change is successful:
        await update.message.reply_text("Sticker title changed")
        with open('stickerpacks', 'wb') as packs:
            old_title = context.user_data["current_pack_title"]
            del stickerpacks[old_title]
            stickerpacks[new_title] = current_pack_name
            pickle.dump(stickerpacks, packs)
    else:
        await update.message.reply_text("Error")
    return ConversationHandler.END

async def get_thumbnail(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Send new thumbnail, make sure it has 1:1 aspect ratio",
                                    reply_markup = ReplyKeyboardRemove())
    return 'set_thumbnail'

async def set_thumbnail(update: Update, context: ContextTypes.DEFAULT_TYPE):
    photo_id = update.message.document.file_id
    image_data = await context.bot.get_file(photo_id)
    image_ready = io.BytesIO()
    await image_data.download_to_memory(image_ready)
    image = Image.open(image_ready)
    width, height = image.size
    if width != height:
        await update.message.reply_text("Uneven ratio")
        return ConversationHandler.END
    else:
        width = height = 100
    image = image.resize((width, height))
    image_io = io.BytesIO()
    image.save(image_io, "PNG")
    successful = True
    user_id = update.effective_user.id
    current_pack_name = context.user_data["current_pack_name"]
    thumb_change = await context.bot.set_sticker_set_thumbnail( user_id = user_id,
                                                                name = current_pack_name,
                                                                thumbnail = image_io.getvalue())
    if thumb_change is successful:
        await update.message.reply_text("Thumbnail changed")
        print(image.size, image.info)
    else:
        await update.message.reply_text("Error")
    return ConversationHandler.END

# FLASK SECTION

app = Flask(__name__)
CORS(app)
app.secret_key = "supa secret"

class Chat:
    def __init__(self, chat_id):
        self.id = chat_id

class User:
    def __init__(self, user_id):
        self.id = user_id

class FlaskUpdate:
    def __init__(self, chat_id, user_id, packname, packtitle, pic, emojis):
        self.effective_chat = Chat(chat_id)
        self.effective_user = User(user_id)
        self.packname = packname
        self.packtitle = packtitle
        self.pic = pic
        self.emojis = emojis

@app.route('/')
def index():
    defaultpage = "
Hello
"
    return defaultpage

@app.route('/getPacks', methods=["GET"])
def getPacks():
    return stickerpacks

@app.route('/updateFromExtension', methods=["POST"])
async def updateFromExtension():
    request_data = request.get_json()
    packtitle = request_data["packname"]
    packname = request_data["packtitle"] #fix title and name discrepancy
    pic = request_data["pic"]
    emojis = request_data["emojis"]
    print(packname, packtitle, pic, emojis)
    ExtensionUpdate = FlaskUpdate(chat_id = IDs["chat_id"],
                                  user_id = IDs["user_id"],
                                  packtitle = packtitle,
                                  packname = packname,
                                  pic = pic,
                                  emojis = emojis)
    await application.update_queue.put(ExtensionUpdate)
    return "True"

async def addFromExtension(update, context: ContextTypes.DEFAULT_TYPE):
    current_image = update.pic
    if current_image.startswith("data:image"):
        with urlopen(current_image) as response:
            current_image = response.read()
    else:
        current_image = requests.get(current_image).content
    image = io.BytesIO(current_image)
    image = Image.open(image)
    image = image.crop(image.getbbox())  # trim transparent pixels
    width, height = image.size
    if width > height:
        ratio = height / width
        width = 512
        height = int(512 * ratio)
    elif width < height:
        ratio = width / height
        height = 512
        width = int(512 * ratio)
    else:
        width = height = 512
    image = image.resize((width, height))
    image_io = io.BytesIO()
    image.save(image_io, "PNG")
    current_emojis = emoji.distinct_emoji_list(update.emojis)
    if current_emojis == []:
        current_emojis = ["ðŸ˜¢", "ðŸ˜¥"]
    input_sticker = telegram.InputSticker(sticker=image_io.getvalue(),
                                          emoji_list=current_emojis)
    successful = True
    user_id = update.effective_user.id
    chat_id = update.effective_chat.id
    current_pack_name = update.packname
    insertion = await context.bot.add_sticker_to_set(user_id=user_id,
                                                     name=current_pack_name,
                                                     sticker=input_sticker)
    if insertion is successful:
        current_stickerset = await context.bot.get_sticker_set(current_pack_name)
        sticker_id = current_stickerset.stickers[-1].file_id
        await context.bot.send_sticker(chat_id=update.effective_chat.id,
                                       sticker=sticker_id)
        await context.bot.send_message(chat_id=chat_id,
                                       text=f"Added to {current_pack_name}")
    else:
        await context.bot.send_message(chat_id=chat_id,
                                       text="Error while adding sticker from extension")

if __name__ == '__main__':

    with open("token.txt", "r") as file:
        mytoken = file.read()
    application = ApplicationBuilder().token(mytoken).build()
    # HANDLERS

    # CONVERSATION HANDLERS
        # newpack
    newpack_handler = MessageHandler(filters.TEXT, newpack)
        # choosing pack
    start_handler = CommandHandler('start', start)
        # get_pack
    get_pack_handler = CallbackQueryHandler(get_pack)
        # new sticker
    sticker_attachment_handler = MessageHandler(filters.ATTACHMENT, new_sticker_attachment)
    newsticker_handler = MessageHandler(filters.Text([ACTIONS["add sticker"]]), newsticker)
    new_sticker_from_7tv_handler = MessageHandler(filters.Regex('7tv.app/emotes') &
                                                  filters.Entity(MessageEntity.URL),
                                                  new_sticker_from_7tv)
    new_sticker_from_link_handler = MessageHandler( filters.TEXT &
                                                    filters.Entity(MessageEntity.URL),
                                                    new_sticker_from_link)
        # cancel
    cancel_handler = CommandHandler('cancel', cancel)
        # thumbnail
    thumbnail_handler = MessageHandler(filters.Text([ACTIONS["thumbnail"]]), get_thumbnail)
    set_thumbnail_handler = MessageHandler(filters.ATTACHMENT, set_thumbnail)
        # new name
    new_pack_title_handler = MessageHandler(filters.Text([ACTIONS["title"]]), new_pack_title)
    set_new_pack_title_handler = MessageHandler(filters.TEXT & ~filters.COMMAND, set_new_pack_title)
        # delete sticker
    delete_sticker_handler = MessageHandler(filters.Text([ACTIONS["delete sticker"]]), delete_sticker)
    pick_delete_sticker_handler = MessageHandler(filters.Sticker.ALL, pick_delete_sticker)
        # emoji
    change_emoji_handler = MessageHandler(filters.TEXT, change_emoji)
    emoji_sticker_handler = MessageHandler(filters.Sticker.ALL, emoji_for_sticker)
    sticker_emoji_handler = MessageHandler(filters.Text([ACTIONS["emoji"]]), sticker_for_emoji)
        # packselect(callback query)
    pack_select_handler = CallbackQueryHandler(pack_select)

    #  conversation
    conv_handler = ConversationHandler(
        entry_points=[start_handler],
        states={
            'get_pack': [get_pack_handler],
            'processing': [newsticker_handler, thumbnail_handler, new_pack_title_handler, delete_sticker_handler, sticker_emoji_handler],
            'new_sticker_methods': [sticker_attachment_handler, new_sticker_from_7tv_handler, new_sticker_from_link_handler],
            'set_thumbnail': [set_thumbnail_handler],
            'set_new_pack_title': [set_new_pack_title_handler],
            'pick_delete_sticker': [pick_delete_sticker_handler],
            'change_emoji': [change_emoji_handler],
            'emoji_for_sticker': [emoji_sticker_handler],
            'newpack': [newpack_handler],
            'pack_select': [pack_select_handler]
        },
        fallbacks=[cancel_handler],
        name="my_conversation"
    )
    application.add_handler(conv_handler)

    # extension
    def flaskthread():
        app.run()
    t = threading.Thread(target=flaskthread)
    t.start()
    extension_handler = TypeHandler(FlaskUpdate, addFromExtension)
    application.add_handler(extension_handler)

    # Other handlers
    unknown_handler = MessageHandler(filters.COMMAND, unknown)
    application.add_handler(unknown_handler)

    application.run_polling()


from django.conf.urls.defaults import *
from django.conf import settings
from django.contrib import admin

#admin.autodiscover()

urlpatterns = patterns('ui.views',
    url(r'^$', 'index',
        name='index'),
    url(r'^login/$', 'login_user',
        name='login'),
    url(r'^logout/$', 'logout_user',
        name='logout'),
    url(r'^user/$', 'implicit_overview',
        name='resolve_user'),
    url(r'^user/overview/', 'global_user_overview',
        name='global_user_overview'),
    url(r'^user/id/(?P<uid>\d+)/$', 'overview_by_uid',
        name='user_by_id'),
    url(r'^user/name/(?P<username>[\w\-\.\d]+)/$', 'overview',
        name='user_by_name'),
    url(r'^user/(?P<username>[\w\-\.\d]+)/update_profile/$', 'update_profile',
        name='update_profile'),
    url(r'^user/(?P<username>[\w\-\.\d]+)/add_expertise/$', 'add_expertise',
        name='add_expertise'),
    url(r'^answer/(?P<domain_id>\d+)/next_question$', 'next_question',
        name='next_question'),
    url(r'^batches/$', 'user_batches',
        name='user_batches'),
    url(r'^batch/check_new/$', 'check_for_new_batches',
        name='check_for_new_batches'),
    url(r'^batch/(?P<batch_id>\d+)/overview/$', 'batch_overview',
        name='batch_overview'),
    url(r'^batch/(?P<batch_id>\d+)/allocate/$', 'batch_panel',
        name='allocate_batch'),
    url(r'^batch/get_allocation_suggestions/$', 'get_allocation_suggestions',
        name='get_allocation_suggestions'),
    url(r'^batch/commit_allocations/$', 'commit_allocations',
        name='commit_allocations'),
    url(r'^import/schema-map/questions/$', 'import_schema_map_questions',
        name='import_schema_map_questions'),
    url(r'^tamer/$', 'redirect_to_tamer',
        name='redirect_to_tamer'),
    url(r'^about/$', 'about',
        name='about'),
    url(r'^thanks/$', 'thanks_and_goodbye',
        name='thanks'),
    url(r'^domain/$', 'domain_overview',
        name='domain'),
    url(r'^domain/(?P<domain_id>\d+)/$', 'domain_details',
        name='domain_details'),
    url(r'^domain/(?P<domain_id>\d+)/avail-data$', 'avail_data',
        name='avail_data'),
    url(r'^domain/(?P<domain_id>\d+)/price-data$', 'price_data',
        name='price_data'),
    url(r'^denied', 'access_denied',
        name='access_denied'),
    url(r'^consent', 'consent',
        name='consent'),
    url(r'^noconsent', 'noconsent',
        name='noconsent'),
    url(r'^training', 'training',
        name='training'),
    url(r'^finished_training', 'finished_training',
        name='finished_training')
)

#urlpatterns += patterns('',
#    url(r'^admin/', include(admin.site.urls)),
#)

if settings.DEBUG:
    urlpatterns += patterns('',
        url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
            {'document_root': settings.STATIC_DOC_ROOT}),
    )


from unicodedata import name
import gurobipy as gp
from gurobipy import GRB
import numpy as np
import scipy.sparse as sp

# Constant Setting
f = [400, 414, 326]
a = [18, 25, 20]
C = [[22, 33, 24],
    [33, 23, 30],
    [20, 25, 27]]
# D = [206+40, 274+40, 220+40]
dl = [206, 274, 220]
du = [40, 40, 40]
k = 0 # count iteration
############################# MASTER PROBLEM START ###########################################
MP = gp.Model()
x = MP.addVars(3,3, lb=0, vtype=GRB.CONTINUOUS, name='x_0')
y = MP.addVars(len(f), lb=0, ub=1, obj=f, vtype=GRB.BINARY, name='y')
z = MP.addVars(len(a), lb=0, obj=a, vtype=GRB.CONTINUOUS, name='z')
g = MP.addVars(3, lb=0, ub=1, name='g')
d = MP.addVars(3, lb=0, name='d')
eta = MP.addVar(obj=1, lb=0, name='η')

MP_Cons_1 = MP.addConstrs((z[i] <= 800*y[i] for i in range(3)), name='MP_Cons_1')
MP_Cons_2 = MP.addConstr((gp.quicksum(z[i] for i in range(3)) >= 772), name='MP_Cons_2')

# iteration constraints
MP_Cons_3 = MP.addConstrs((gp.quicksum(x[i,j] for j in range(3)) <= z[i] for i in range(3)), name='MP_Cons_3')
MP_Cons_4 = MP.addConstrs((gp.quicksum(x[i, j] for i in range(3)) >= d[j] for j in range(3)), name='MP_Cons_4')
MP_Cons_eta = MP.addConstr(eta >= gp.quicksum(x[i,j]*C[i][j] for i in range(3) for j in range(3)), name='MP_Cons_eta')

# Master-problem uncertainty constraints
MP_Cons_uncertainty_1 = MP.addConstrs((d[i] == dl[i]+du[i]*g[i] for i in range(3)), name='MP_Uncertainty_Cons1')
MP_Cons_uncertainty_2 = MP.addConstr((gp.quicksum(g[i] for i in range(3)) <= 1.8), name='MP_Uncertainty_Cons2')
MP_Cons_uncertainty_3 = MP.addConstr((gp.quicksum(g[i] for i in range(2)) <= 1.2), name='MP_Uncertainty_Cons3')

MP.optimize()

LB = MP.objval
############################# MASTER PROBLEM END ###########################################

############################# SUB PROBLEM START ###########################################

SP = gp.Model()
x_sub = SP.addVars(3, 3, lb=0, vtype=GRB.CONTINUOUS, name='x_sub')
d_sub = SP.addVars(3, lb=0, name='d_sub')
g_sub = SP.addVars(3, lb=0, ub=1, name='g_sub')
pi = SP.addVars(6, lb=0, vtype=GRB.CONTINUOUS, name='pi')
v = SP.addVars(6, vtype=GRB.BINARY, name='v')
w = SP.addVars(3, 3, vtype=GRB.BINARY, name='w')
M = 10000

# Constraints
SP_Cons_1 = SP.addConstrs((gp.quicksum(x_sub[i,j] for j in range(3)) <= z[i].x for i in range(3)), name='SP_Cons_1') #[0:3]
SP_Cons_2 = SP.addConstrs((gp.quicksum(x_sub[i,j] for i in range(3)) >= d_sub[j] for j in range(3)), name='SP_Cons_2') #[3:6]
SP_Cons_3 = SP.addConstrs((-pi[i]+pi[j+3] <= C[i][j] for i in range(3) for j in range(3)), name='SP_Cons_3') #[6:15]

# slack constraints part 1
SP_SLACK_CONS_1 = SP.addConstrs((z[i].x-gp.quicksum(x_sub[i,j] for j in range(3)) <= M*(1-v[i]) for i in range(3)), name='SP_SLACK_CONS_1') #[15:18]
SP_SLACK_CONS_2 = SP.addConstrs((gp.quicksum(x_sub[i,j] for i in range(3))-d_sub[j] <= M*(1-v[j+3]) for j in range(3)), name='SP_SLACK_CONS_2') #[18:21]
SP_SLACK_CONS_3 = SP.addConstrs((pi[i] <= M*v[i] for i in range(6)), name='SP_SLACK_CONS_3') # [21:27]

# slack constraints part 2
SP_SLACK_CONS_4 = SP.addConstrs((C[i][j]+pi[i]-pi[j+3] <= M*(1-w[i,j]) for i in range(3) for j in range(3)), name='SP_SLACK_CONS_4') # [27:36]
SP_SLACK_CONS_5 = SP.addConstrs((x_sub[i,j] <= M*w[i,j] for i in range(3) for j in range(3)), name='SP_SLACK_CONS_5') # [36:45]

# uncertainty
SP_Cons_uncertainty_1 = SP.addConstrs((d_sub[i] == dl[i]+du[i]*g_sub[i] for i in range(3)), name='SP_Uncertainty_Cons1') #[45:48]
SP_Cons_uncertainty_2 = SP.addConstr((gp.quicksum(g_sub[i] for i in range(3)) <= 1.8), name='MP_Uncertainty_Cons2') #[48]
SP_Cons_uncertainty_3 = SP.addConstr((gp.quicksum(g_sub[i] for i in range(2)) <= 1.2), name='MP_Uncertainty_Cons3') #[49]

sub_obj = gp.quicksum(C[i][j]*x_sub[i,j] for i in range(3) for j in range(3))
SP.setObjective(sub_obj, GRB.MAXIMIZE)
SP.optimize()
SP_objval = SP.objval
UB = LB - eta.x + SP_objval
print(UB)
############################# SUB PROBLEM END ###########################################

############################# CCG START ###########################################
while np.abs(UB-LB)>1e-5 :
    k = k+1
    # Master-problem
    x_new = MP.addVars(3,3, lb=0, vtype=GRB.CONTINUOUS, name='x_{0}'.format(k))
    MP_Cons_3_new = MP.addConstrs((gp.quicksum(x_new[i,j] for j in range(3)) <= z[i] for i in range(3)), name='MP_Cons_3_{}'.format(k))
    MP_Cons_4_new = MP.addConstrs((gp.quicksum(x_new[i, j] for i in range(3)) >= d_sub[j].x for j in range(3)), name='MP_Cons_4_{}'.format(k))
    MP_Cons_eta = MP.addConstr(eta >= gp.quicksum(x_new[i,j]*C[i][j] for i in range(3) for j in range(3)), name='MP_Cons_eta_{}'.format(k))
    MP.optimize()
    LB = max(LB, MP.objval)
    print(LB)

    # Sub-problem update
    # delete old constraints which related to z
    SP.remove(SP.getConstrs()[0:3])
    SP.remove(SP.getConstrs()[15:18])
    # add new constraints which related to z
    SP_Cons_1 = SP.addConstrs((gp.quicksum(x_sub[i,j] for j in range(3)) <= z[i].x for i in range(3)), name='SP_Cons_1') #[0:3]
    SP_SLACK_CONS_1 = SP.addConstrs((z[i].x-gp.quicksum(x_sub[i,j] for j in range(3)) <= M*(1-v[i]) for i in range(3)), name='SP_SLACK_CONS_1') #[15:18]

    SP.optimize()
    UB = LB - eta.x + SP.objval
    print(UB)
############################# CCG END ###########################################

# Some informations
print("Iteration finished! We found the optimal solution!")
print("Final Objective:{0}".format(LB))
print(y[0],y[1],y[2])
print(z[0],z[1],z[2])
for i in range(3):
    for j in range(3):
        print(x_new[i,j])


from django import http
from django.db import models
from databrowse.datastructures import EasyModel
from databrowse.sites import DatabrowsePlugin
from django.shortcuts import render_to_response
from django.utils.text import capfirst
from django.utils.encoding import smart_str, force_unicode, iri_to_uri
from django.utils.safestring import mark_safe
import urllib

class FieldChoicePlugin(DatabrowsePlugin):
    def __init__(self, field_filter=None):
        # If field_filter is given, it should be a callable that takes a
        # Django database Field instance and returns True if that field should
        # be included. If field_filter is None, that all fields will be used.
        self.field_filter = field_filter

    def field_dict(self, model):
        """
        Helper function that returns a dictionary of all fields in the given
        model. If self.field_filter is set, it only includes the fields that
        match the filter.
        """
        if self.field_filter:
            return dict([(f.name, f) for f in model._meta.fields if self.field_filter(f)])
        else:
            return dict([(f.name, f) for f in model._meta.fields if not f.rel and not f.primary_key and not f.unique and not isinstance(f, (models.AutoField, models.TextField))])

    def model_index_html(self, request, model, site):
        fields = self.field_dict(model)
        if not fields:
            return u''
        return mark_safe(u'
View by: %s
' % \
            u', '.join(['%s' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))

    def urls(self, plugin_name, easy_instance_field):
        if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
            field_value = smart_str(easy_instance_field.raw_value)
            #if easy_instance_field.field.name!='url':
            if not isinstance(easy_instance_field.field, models.URLField):
                return [mark_safe(u'%s%s/%s/%s/' % (
                   easy_instance_field.model.url(),
                   plugin_name, easy_instance_field.field.name,
                   urllib.quote(field_value, safe='')))]
            else:
                return [iri_to_uri(field_value)]

    def model_view(self, request, model_databrowse, url):
        self.model, self.site = model_databrowse.model, model_databrowse.site
        self.fields = self.field_dict(self.model)

        # If the model has no fields with choices, there's no point in going
        # further.
        if not self.fields:
            raise http.Http404('The requested model has no fields.')

        if url is None:
            return self.homepage_view(request)
        url_bits = url.split('/', 1)
        if self.fields.has_key(url_bits[0]):
            return self.field_view(request, self.fields[url_bits[0]], *url_bits[1:])
        else:
            field = self.model._meta.get_field(url_bits[0])
            return self.field_view(request, field, *url_bits[1:])

        raise http.Http404('The requested page does not exist.')

    def homepage_view(self, request):
        easy_model = EasyModel(self.site, self.model)
        field_list = self.fields.values()
        field_list.sort(lambda x, y: cmp(x.verbose_name, y.verbose_name))
        return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list, 'request': request})

    def field_view(self, request, field, value=None):
        easy_model = EasyModel(self.site, self.model)
        easy_field = easy_model.field(field.name)
        if value is not None:
            obj_list = easy_model.objects(**{field.name: value})
            return render_to_response('databrowse/fieldchoice_detail.html', {'root_url': self.site.root_url, 'model': easy_model, 'field': easy_field, 'value': value, 'object_list': obj_list, 'request': request})
        obj_list = [v[field.name] for v in self.model._default_manager.distinct().order_by(field.name).values(field.name)]
        if '' in obj_list:
            obj_list.remove('')
        return render_to_response('databrowse/fieldchoice_list.html', {'root_url': self.site.root_url, 'model': easy_model, 'field': easy_field, 'object_list': obj_list, 'request': request})


import _flowunit as modelbox
import numpy as np
import cv2

class CustomResize(modelbox.FlowUnit):
    def __init__(self):
        super().__init__()

    def open(self, config):
        self.max_edge = config.get_int("max_edge", 320)
        return modelbox.Status.StatusCode.STATUS_SUCCESS

    def process(self, data_context):
        in_image_list = data_context.input("in_image")
        out_image_list = data_context.output("out_image")

        for buffer_img in in_image_list:
            width = buffer_img.get("width")
            height = buffer_img.get("height")
            channel = buffer_img.get("channel")

            img_data = np.array(buffer_img.as_object(), dtype=np.uint8)
            img_data = img_data.reshape(height, width, channel)

            im_size_min = np.min([height, width])
            im_size_max = np.max([height, width])
            resize = self.max_edge / float(im_size_min)
            if np.round(resize * im_size_max) > self.max_edge:
                resize = self.max_edge / float(im_size_max)

            resize_img = cv2.resize(img_data, None, None, fx=resize, fy=resize, interpolation=cv2.INTER_LINEAR)
            resize_img_height, resize_img_width, _ = resize_img.shape

            add_buffer = modelbox.Buffer(self.get_bind_device(), resize_img)
            add_buffer.copy_meta(buffer_img)
            add_buffer.set("width", resize_img_width)
            add_buffer.set("height", resize_img_height)
            add_buffer.set("width_stride", resize_img_width)
            add_buffer.set("height_stride", resize_img_height)
            out_image_list.push_back(add_buffer)

        return modelbox.Status.StatusCode.STATUS_SUCCESS

    def close(self):
        return modelbox.Status()

    def data_pre(self, data_context):
        return modelbox.Status()

    def data_post(self, data_context):
        return modelbox.Status()


import random
from datetime import datetime

from dateutil.relativedelta import relativedelta

from function import global_value as g

def help(command):
    msg = f"```使い方："
    msg += f"\n\t{command} help          このメッセージ"
    msg += f"\n\t{command} results       成績出力"
    msg += f"\n\t{command} record        張り付け用集計済みデータ出力"
    msg += f"\n\t{command} graph         ポイント推移グラフを表示"
    msg += f"\n\t{command} ranking       ランキングを表示"
    msg += f"\n\t{command} member        登録されているメンバー"
    msg += f"\n\t{command} add | del     メンバーの追加/削除"
    msg += f"\n\t{command} load | save   メンバーリストの再読み込み/保存"
    msg += f"```"
    return(msg)

def invalid_argument():
    return(random.choice([
        f"えっ？",
        f"すみません、よくわかりません。",
        f"困らせないでください。",
    ]))

def invalid_score(user_id, score, pointsum):
    return(random.choice([
        f"<@{user_id}> {abs(pointsum - score) * 100}点合わないようです。",
        f"<@{user_id}> {abs(pointsum - score) * 100}点合いませんよ。",
        f"<@{user_id}> {abs(pointsum - score) * 100}点合いません。ご確認を。",
        f"<@{user_id}> {abs(pointsum - score) * 100}点合ってませんね。",
    ]))

def no_hits(starttime, endtime):
    keyword = g.config["search"].get("keyword", False)
    if keyword:
        return(
            "{} ～ {} に{}はありません。".format(
                starttime.strftime('%Y/%m/%d %H:%M'),
                endtime.strftime('%Y/%m/%d %H:%M'),
                keyword,
            )
        )
    else:
        return("見つかりません。")

def remarks(command_option, starttime):
    ret = ""
    remark = []

    if not command_option["playername_replace"]:
        remark.append("名前ブレ修正なし")
    if not command_option["guest_skip"]:
        remark.append("2ゲスト戦の結果を含む")
    if not command_option["unregistered_replace"]:
        remark.append("ゲスト置換なし(※：未登録プレイヤー)")
    if remark:
        ret = f"特記：" + "、".join(remark)

    retention_period = g.config["search"].getint("retention_period", 0)
    if not command_option["archive"] and retention_period != 0:
        limittime = datetime.now() - relativedelta(days = retention_period)

        if  starttime < limittime:
            if ret:
                ret += "\n"
            ret += f"注記：検索開始日がログの保存期間を越えています"

    return(ret)


from django.contrib import admin
from home.models import Contact,Signup, Login,Complaint
class LoginAdmin(admin.ModelAdmin):
    list_display = ('id', 'uname', 'psw')

class SignupAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'surname','phone', 'email','add','file', 'pwd','rpwd')
    list_display_links =('id','name','surname')
    search_fields=('name','surname')

class ComplaintAdmin(admin.ModelAdmin):
    list_display = ( 'id','name', 'place','Description', 'PhysicalEvidence','fileUpload')
    list_display_links =('id','name','place')
    search_fields=('id','name')

# Register your models here.
admin.site.register(Contact)
admin.site.register(Signup,SignupAdmin)
admin.site.register(Login,LoginAdmin)
admin.site.register(Complaint,ComplaintAdmin)


from collections import Counter
from itertools import chain
from typing import List, Optional, Tuple, Union

import numpy as np
import pandas as pd
import tqdm
from gensim.models import KeyedVectors
from sklearn.decomposition import TruncatedSVD

from tweet_recommendations.other_methods.method import Method
from tweet_recommendations.utils.clients import get_wcrft2_results_for_text

class SIFEmbedding(Method):

    def __init__(self, path_to_keyedvectors_model: Optional[str] = None,
                 tweet_embedding_name: Optional[str] = "embedding",
                 words_embeddings_name: Optional[str] = "word_embeddings",
                 verbose: bool = False):
        """
        Method for creating embeddings out of words and combining them into single tweet embedding with modified
        weighted average of those embeddings.
        :param path_to_keyedvectors_model: Path to converted by script `convert_embedding_model_to_mmap.py` gensim
            model. It can either word2vec or fasttext, `gensim` handles both.
        :param tweet_embedding_name: Optional, name of used embedding method.
                Required when `path_to_keyedvectors_model` is not provided.
                Input DataFrame in `fit` method will have to contain column
                named the same as value provided to this argument.
                If `path_to_keyedvectors_model` is provided `embedding_name`
                is ignored and is just used as a name.
        :param words_embeddings_name: Optional, name of used embedding method.
                Required when `path_to_keyedvectors_model` is not provided.
                Input DataFrame in `fit` method will have to contain column
                named the same as value provided to this argument. Every column
                element should contain a list of word embedding for a given sentence.
                If `path_to_keyedvectors_model` is provided `words_embeddings_name`
                is ignored and is just used as a name.
        :param verbose: Whether method should be verbose.
        """
        self.verbose = verbose
        self.keyed_vector_model = None
        self.embedding_name = tweet_embedding_name
        self.words_embeddings_name = words_embeddings_name

        if path_to_keyedvectors_model:
            if self.verbose:
                print("Loading keyed vectors model ...")
            self.keyed_vector_model = KeyedVectors.load(path_to_keyedvectors_model, mmap="r")
        elif self.verbose:
            print("No keyed vector found. \n Embeddings will be acquired from the input data ...")

        self.words_weights = None
        self._pc = None

    def fit(self, x: pd.DataFrame, y: Optional[pd.DataFrame] = None,
            **fit_params) -> "SIFEmbedding":
        """

        :param x: DataFrame which should contain tweet text lemmas
        :param y: None for compatibility
        :param fit_params: min_word_occurrences: int. Parameter limiting/filtering infrequent words.
                           smoothing: int. Smoothing value parameter.
                           random_state: int. Parameter used for calculating principal components.

        :return: self
        """
        min_word_occurence = fit_params.get("min_word_occurences", 0)
        smoothing = fit_params.get("smoothing", 1)

        all_words_in_dataset = list(chain.from_iterable(x["lemmas"].values))
        word_counts = dict(Counter(all_words_in_dataset))
        if min_word_occurence > 0:
            word_counts = {lemma: count for lemma, count in
                           word_counts.items() if count >= min_word_occurence}

        total_words_count = len(all_words_in_dataset)
        self.words_weights = {
            word.lower(): smoothing / (smoothing + (count / total_words_count))
            for word, count
            in word_counts.items()}

        if self.keyed_vector_model is None:
            assert self.embedding_name in x.columns, \
                "When no embedding model provided, input DataFrame should " \
                f"contain column named same as `{self.embedding_name}`."
        else:
            assert "lemmas" in x.columns, \
                "When embedding model is provided input DataFrame should " \
                'contain column "lemmas".'

        emb = self._get_weighted_average_embeddings(x)

        self._pc = self._compute_pc(emb, fit_params.get("random_state", None))

        return self

    def transform(self, x: Union[Tuple[Tuple[str, ...]], Tuple[str, ...], pd.DataFrame], **kwargs) -> np.ndarray:
        """
        For a given tweet represented as a list of lemmas recommends hashtags.
        :param x: list of list of str or list of str. If first argument of x is a list is str, it is assumed that list
            contains already lemmatized text. If single str is present as first element, it is assumed
            that lemmatization has to be performed. A DataFrame can be passed if it contains lemmatized sentences with
            its corresponding embeddings.
        :return: np.ndarray of sentences embeddings.
        """
        if self.keyed_vector_model:
            lemmatized = []
            if isinstance(x, pd.DataFrame):
                lemmatized = x["lemmas"].to_list()
            elif isinstance(x[0], str):
                for i, xi in enumerate(x):
                    lemmatized.append(get_wcrft2_results_for_text(xi))
            elif isinstance(x[0], list):
                for i, xi in enumerate(x):
                    lemmatized.append(xi)
            data = lemmatized
        else:
            data = x
        output = self._get_sif_embedding(data)
        return np.asarray(output)

    def _get_weighted_average_embeddings(self, data: Union[List[List[str]], pd.DataFrame]) -> np.ndarray:
        """
        Compute the weighted average vectors for given tweet content lemmas.
        :param data: DataFrame which should contain tweet text lemmas.
        :return: Weighted average of tweet embeddings.
        """
        if isinstance(data, pd.DataFrame):
            sentences = data["lemmas"].values.tolist()
        else:
            sentences = data
        n_samples = len(sentences)
        if self.keyed_vector_model:
            embedding_size = self.keyed_vector_model.vector_size
        else:
            embedding_size = len(data[self.embedding_name].iloc[0])
        emb = np.zeros((n_samples, embedding_size))

        for i in tqdm.trange(n_samples, disable=not self.verbose):
            if sentences[i]:
                words_embeddings = []
                words_weights = []
                for word in sentences[i]:
                    try:
                        if self.keyed_vector_model:
                            word_embedding = self.keyed_vector_model.word_vec(word.lower())
                            words_embeddings.append(word_embedding)
                        words_weights.append(self.words_weights.get(word.lower(), 0))
                    except KeyError:
                        continue
                if not self.keyed_vector_model:
                    words_embeddings = data[self.words_embeddings_name].iloc[i]
                sentence_word_embeddings = np.asarray(words_embeddings)
                sentence_word_weights = np.array(words_weights)

                if len(sentence_word_weights) > 0:
                    emb[i, :] = sentence_word_weights.dot(sentence_word_embeddings) / len(
                        sentence_word_weights)
        return emb

    def _compute_pc(self, tweets_embeddings: np.ndarray, random_state) -> np.ndarray:
        """
        Compute the first principal components for given tweet embedding matrix.
        :param tweets_embeddings: array of tweet embeddings.
        :return: svd.component_: array of first pca principal component
        for each tweet embedding.
        """
        svd = TruncatedSVD(n_components=1, n_iter=7, random_state=random_state)
        svd.fit(tweets_embeddings)
        return svd.components_

    def _remove_pc(self, tweets_embeddings: np.ndarray) -> np.ndarray:
        """
        Remove the projection on the principal components for each tweet embedding.
        :param tweets_embeddings: array of tweet embeddings.
        :return: tweets embeddings after removing its projection.
        """
        tweet_embeddings_with_first_pc_removed = tweets_embeddings - tweets_embeddings.dot(
            self._pc.transpose()) * self._pc
        return tweet_embeddings_with_first_pc_removed

    def _get_sif_embedding(self, data: Union[List[List[str]], pd.DataFrame]) -> np.ndarray:
        """
        Compute tweet content embedding using weighted average with
        removing the projection on the first principal component
        :param data: sentences is a list of lemmatized tweet word list or DataFrame
        with list of lemmatized tweet word list and its embeddings
        :return: emb is a array of tweets embeddings
        """
        emb = self._get_weighted_average_embeddings(data)
        emb = self._remove_pc(emb)
        return emb