### Code for Mistic
### Image t-SNE viewer for multiplexed images
###
### code author Sandhya Prabhakaran
###
### Revision history
### FoV code ###
### Jan 29th 2021
### 25th June 2021
### github version
### 19th Jan 2022
### added Patient id live canvas
### 26th Jan 2022 ###
### merging the stack montage code
### prior version is main_rollback.
### 14th Feb 2022
### adding codex, tcycif, vectra option
### prior version is main_rollback_1
### 14th mar 2022
### added the user tsne as well 'arrange in rows' (seeall) tsne generation code

## 20th Feb 2022
## this is the latest main.py, with dates and comments removed
## for github upload

## 8th April 2022
## 2nd rebuttal updates
## tsne and dpmm code added

#### 21st April 2022
#### CyCIF files
## code cleanup for github

import os
import sys
import random
import warnings

import numpy as np
import pandas as pd

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

from matplotlib.figure import Figure

from matplotlib import cm

from scipy import ndimage

from sklearn.manifold import TSNE

import phenograph as pg
from scipy.stats import zscore

from skimage.color import rgb2gray

from matplotlib.patches import Polygon

from skimage import io

from skimage import data
from skimage.filters import threshold_otsu
from skimage.segmentation import clear_border
from skimage.measure import label, regionprops
from skimage.morphology import closing, square
from skimage.color import label2rgb
from skimage.io import imread, imshow, imread_collection, concatenate_images
from skimage.transform import resize
from skimage.morphology import label

import seaborn as sns

import scipy.spatial

from scipy.spatial import distance

import seaborn as sns

import matplotlib.image as mpimg

import matplotlib.colors as colors

from matplotlib import colors as mcolors

colors = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)

from PIL import Image, ImageOps

from bokeh.layouts import column, row
from bokeh.models import (Select, Button)
from bokeh.palettes import Spectral5
from bokeh.plotting import curdoc, figure
from bokeh.models.widgets import Div
from bokeh.layouts import column, layout

from bokeh.models import (HoverTool, ColumnDataSource)
from bokeh.models.widgets import RadioButtonGroup
from bokeh.models import CheckboxGroup
from bokeh.models import CustomJS
from bokeh.models import BoxSelectTool
from bokeh.themes import Theme

from bokeh.models.widgets import Panel, Tabs
from bokeh.io import output_file, show

##
import skimage.io as io
import tifffile
from PIL import TiffImagePlugin

from sklearn.mixture import BayesianGaussianMixture

### image-tSNE

width = 5000
height = 5000
max_dim = 500
tw =1200
th = 900

full_image_1 = Image.new('RGBA', (width, height))

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

################## Function definitions ######################

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

#######################################################################
#####  get_cell_coords(), get_neighbours(), point_valid(),         ####
#####  get_point() are functions to generate random points         ####
#####  functions modified from:                                    ####
#####  https://scipython.com/blog/poisson-disc-sampling-in-python/ ####
#######################################################################

def get_cell_coords(pt,a):
    """Get the coordinates of the cell that pt = (x,y) falls in."""

    return int(pt[0] // a), int(pt[1] // a)

def get_neighbours(coords,nx,ny,cells):
    """Return the indexes of points in cells neighbouring cell at coords.
    For the cell at coords = (x,y), return the indexes of points in the cells
    with neighbouring coordinates illustrated below: ie those cells that could
    contain points closer than r.
                                     ooo
                                    ooooo
                                    ooXoo
                                    ooooo
                                     ooo
    """

    dxdy = [(-1,-2),(0,-2),(1,-2),(-2,-1),(-1,-1),(0,-1),(1,-1),(2,-1),
            (-2,0),(-1,0),(1,0),(2,0),(-2,1),(-1,1),(0,1),(1,1),(2,1),
            (-1,2),(0,2),(1,2),(0,0)]
    neighbours = []
    for dx, dy in dxdy:
        neighbour_coords = coords[0] + dx, coords[1] + dy
        if not (0 <= neighbour_coords[0] < nx and
                0 <= neighbour_coords[1] < ny):
            # We're off the grid: no neighbours here.
            continue
        neighbour_cell = cells[neighbour_coords]
        if neighbour_cell is not None:
            # This cell is occupied: store this index of the contained point.
            neighbours.append(neighbour_cell)
    return neighbours

def point_valid(pt,a,nx,ny,cells,samples,r):
    """Is pt a valid point to emit as a sample?
    It must be no closer than r from any other point: check the cells in its
    immediate neighbourhood.
    """

    cell_coords = get_cell_coords(pt,a)
    for idx in get_neighbours(cell_coords,nx,ny,cells):
        nearby_pt = samples[idx]
        # Squared distance between or candidate point, pt, and this nearby_pt.
        distance2 = (nearby_pt[0]-pt[0])**2 + (nearby_pt[1]-pt[1])**2
        if distance2 < r**2:
            # The points are too close, so pt is not a candidate.
            return False
    # All points tested: if we're here, pt is valid
    return True

def get_point(k, refpt,r,a,nx,ny,cells,samples):
    """Try to find a candidate point relative to refpt to emit in the sample.
    We draw up to k points from the annulus of inner radius r, outer radius 2r
    around the reference point, refpt. If none of them are suitable (because
    they're too close to existing points in the sample), return False.
    Otherwise, return the pt.
    """
    i = 0
    while i < k:
        rho, theta = np.random.uniform(r, 2*r), np.random.uniform(0, 2*np.pi)
        pt = refpt[0] + rho*np.cos(theta), refpt[1] + rho*np.sin(theta)
        if not (0 <= pt[0] < width and 0 <= pt[1] < height):
            # This point falls outside the domain, so try again.
            continue
        if point_valid(pt,a,nx,ny,cells,samples,r):
            return pt
        i += 1
    # We failed to find a suitable point in the vicinity of refpt.
    return False

###############################################################
### draw_tSNE_scatter()                                    ####
### a) creates the metadata-based tSNE scatter plots       ####
### b) populates the hover functionality for each tSNE dot ####
###############################################################

def draw_tSNE_scatter(tsne1, file_name_hover,cluster_ms_list ):
    tsne=np.asarray(tsne1)

    source = ColumnDataSource(data=dict(
        x=tsne[:,0],
        y=tsne[:,1],
        pat_list = pat_ind_list,
        res_list = resp_list,
        fov_list = pat_fov_list,
        color_vec_list = color_vec,
        tx_list = tx_list,
        color_vec_tx_list = color_vec_tx,
        clust_asgn_list = clust_asgn_list,
        color_vec_clasgn_list = color_vec_clasgn,
        cluster_anno_list = cluster_anno_list,
        cluster_ms_list = cluster_ms_list,
        cluster_pat_list = cluster_pat_list,
        color_vec_patid_list = color_vec_patid,
        file_name_hover_list = file_name_hover
        #legend_p11 = legend_p1
    ))
    TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"

    TOOLTIPS = [
        ("index", "$index"),
        ("(x,y)", "($x, $y)"),
        ("Pat_id", "@pat_list"),
        ("Response", "@res_list"),
        ("Treatment", "@tx_list"),
        ("Cluster id", "@clust_asgn_list"),
        ("Channel", "@cluster_ms_list"),
        ("Thumbnail", "@file_name_hover_list"),
        ("FoV","@fov_list")
    ]

    p1 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
               title="Patient response")

    p1.scatter('x', 'y', size=10, source=source, legend= 'res_list', color = 'color_vec_list',fill_alpha=0.6)
    p1.legend.location = "bottom_left"

    p2 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
               title="Treatment category")
    p2.scatter('x', 'y', size=10, source=source, legend= 'tx_list', color = 'color_vec_tx_list',fill_alpha=0.6)
    p2.legend.location = "bottom_left"

    p3 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
               title="Cluster annotations")
    p3.scatter('x', 'y', size=10, source=source, legend= 'cluster_anno_list', color = 'color_vec_clasgn_list',fill_alpha=0.6)
    p3.legend.location = "bottom_left"

    p4 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
               title="Patient id")
    p4.scatter('x', 'y', size=10, source=source, legend= 'cluster_pat_list', color = 'color_vec_patid_list',fill_alpha=0.6)
    p4.legend.location = "bottom_left"

    return ([p1,p2,p3,p4, source])

##############################################################################################
### generate_stack_montage()                                                              ####
### a) reads in and processes the image channels                                          ####
### b) generates evenly-spaced points on the static canvas to arrange the images in rows  ####
### c) generates thumbnails, and pastes these onto the static canvas                      ####
### d) stores the thumbnails in the output folder                                         ####
### e) updates the hover tool with thumbnail paths, marker names and metadata             ####
##############################################################################################

##################
def generate_stack_montage(chk_box_marker_sm, rb_imtech_val, LABELS_MARKERS):

    full_image = Image.new('RGBA', (width, height))
    size = [256,256]

    file_name_hover = []
    file_name_hover_list = []

    # Choose up to k points around each reference point as candidates for a new
    # sample point
    k = 10

    # Minimum distance between samples
    r = 2

    width_1, height_1 = 20, 22

    print('Arrange images side-by-side')

    # Cell side length
    a = r/np.sqrt(2)
    # Number of cells in the x- and y-directions of the grid
    nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1

    # A list of coordinates in the grid of cells
    coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]

    #logic to get grid points
    m = np.int(np.floor(nx*ny/num_images))

    row_needed = []
    def multiples(m, num_images):
        for i in range(num_images):
            row_needed.append(i*m)

    multiples(m,num_images)

    select_coords = np.array(coords_list)[np.array(row_needed).flatten()]

    print(type(select_coords))
    ################

    #tsne = np.asarray(coords_list)
    tsne = select_coords
    df_Xtsne = pd.DataFrame(tsne)
    df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_sm.csv'), header=None, index=None)

    # save the tSNE points to bve read later on irrespective of whoch option was chosen
    df_Xtsne_touse = pd.DataFrame(tsne)
    df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse_sm.csv'), header=None, index=None)

    tx, ty = tsne[:,0], tsne[:,1]

    tx[tx ==0] = 0.2
    ty[ty ==0] = 0.1

    tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
    ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
    print('###tx')
    print(tx)
    print(ty)
    inds = range(len(marker_image_list))

    N = len(inds)

    for k in range(len(inds)):

        image_all_2 = []
        image_all_1 = []

        im = Image.open(marker_image_list[inds[k]])

        for m in range(1):
            image = im

            median_filtered = scipy.ndimage.median_filter(image, size=1)
            image_all_2.append(median_filtered)

            # apply threshold

            thresh = threshold_otsu(image_all_2[m])

            print('thresh is: ', str(thresh))

            bw = closing(image > thresh, square(1))

            # remove artifacts connected to image border
            cleared_imtsne = clear_border(bw)

            image_all_1.append(cleared_imtsne*100)

        tl = sum(image_all_1)

        if (rb_imtech_val ==0): #vectra
            tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))

        elif (rb_imtech_val ==1): #t-CyCIF
            tile_1 = Image.fromarray(np.uint8(cm.hsv(tl)*500))
        elif (rb_imtech_val ==2): # CODEX
             tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
        elif (rb_imtech_val ==3): # CyCIF
             tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))

        old_size = tile_1.size

        new_size = (old_size[0]+1, old_size[1]+1)
        new_im = Image.new("RGB", new_size,color='black')

        new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))

        file_name = os.path.join(path_wd+'/output_tiles/sm_image_tsne_tils'+str(k)+'.png')

        file_name_hover.append('sm_image_tsne_tils'+str(k)+'.png')

        new_im.save(file_name)

        # Read Images

        tile_2 = Image.open(file_name)
        rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
        tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias

        full_image.paste(tile_2, (int((width-max_dim)*ty[k]), int((height-max_dim)*tx[k])),mask=tile_2.convert('RGBA'))

    matplotlib.pyplot.figure(figsize = (25,20))
    plt.imshow(full_image)
    plt.axis('off')

    k = random.randint(2,500)
    file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/sm_image_tsne_tils_all_'+str(k)+'.png')
    full_image.save(file_name)

    rotated_img     = full_image.rotate(90)
    file_name_rot = os.path.join(path_wd+'/image_tSNE_GUI/static/sm_image_tsne_tils_all_'+str(k)+'_rot.png')

    rotated_img.save(file_name_rot)

    return([file_name_rot,tsne, file_name_hover])

###########################################################################################################
### generate_image_tSNE()                                                                          ########
###                                                                                ########
### a) reads in and pre-processes the images                                                       ########
### b) generates random points or evenly-spaced points on the static canvas to arrange the images  ########
###                      in rows or reads in the user-provided tSNE                                ########
### c) generates thumbnails based on border choices, pastes the thumbnails onto the static canvas  ########
### d) stores the thumbnails in the output folder                                                  ########
### e) updates the hover tool with thumbnail paths                                                 ########
### f) shuffle or no shuffle option is handled in this function where images are randomly shuffled ########
###########################################################################################################

def generate_image_tSNE(chk_box_marker,rb_val,rb_rs_val,rb_shf_val, rb_imtech_val, mc, wc, LABELS_MARKERS):
    full_image = Image.new('RGBA', (width, height))
    size = [256,256]

    file_name_hover = []
    file_name_hover_list = []

    if (rb_shf_val == 0): # no shuffle option

        if(rb_rs_val==1):
            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), index_col=None, header= None)
            print('usertsne')
        elif(rb_rs_val==0):
            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
            print('origtsne')

        elif(rb_rs_val==2):
            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), index_col=None, header= None)
            print('seealltsne')

        tsne = np.array(df_Xtsne)

        '''
        #create the random tsne projections
        if (rb_rs_val==1):
            # Choose up to k points around each reference point as candidates for a new
            # sample point
            k = 10

            # Minimum distance between samples
            r = 1.7

            width_1, height_1 = 20, 22

            print('Generating random co-ordinates')

            # Cell side length
            a = r/np.sqrt(2)
            # Number of cells in the x- and y-directions of the grid
            nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1

            # A list of coordinates in the grid of cells
            coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
            # Initilalize the dictionary of cells: each key is a cell's coordinates, the
            # corresponding value is the index of that cell's point's coordinates in the
            # samples list (or None if the cell is empty).
            cells = {coords: None for coords in coords_list}

            # Pick a random point to start with.
            pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
            samples = [pt]
            # Our first sample is indexed at 0 in the samples list...
            cells[get_cell_coords(pt,a)] = 0
            # ... and it is active, in the sense that we're going to look for more points
            # in its neighbourhood.
            active = [0]

            nsamples = 1
            # As long as there are points in the active list, keep trying to find samples.
            while (nsamples < num_images): #active:
                # choose a random "reference" point from the active list.
                idx = np.random.choice(active)
                refpt = samples[idx]
                # Try to pick a new point relative to the reference point.
                pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
                if pt:
                    # Point pt is valid: add it to the samples list and mark it as active
                    samples.append(pt)
                    nsamples += 1
                    active.append(len(samples)-1)
                    cells[get_cell_coords(pt,a)] = len(samples) - 1
                    print('nsamples is: ',str(nsamples))
                else:
                    # We had to give up looking for valid points near refpt, so remove it
                    # from the list of "active" points.
                    active.remove(idx)

            tsne = np.asarray(samples)
            df_Xtsne = pd.DataFrame(tsne)
            df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None)

        elif(rb_rs_val==0):

            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
            df_Xtsne.shape
            tsne = np.array(df_Xtsne )

        elif(rb_rs_val==2):

            # Choose up to k points around each reference point as candidates for a new
            # sample point
            k = 10

            # Minimum distance between samples
            r = 2

            width_1, height_1 = 20, 22

            print('Arrange images side-by-side')

            # Cell side length
            a = r/np.sqrt(2)
            # Number of cells in the x- and y-directions of the grid
            nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1

            # A list of coordinates in the grid of cells
            coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]

            #logic to get grid points
            m = np.int(np.floor(nx*ny/num_images))

            row_needed = []
            def multiples(m, num_images):
                for i in range(num_images):
                    row_needed.append(i*m)

            multiples(m,num_images)

            select_coords = np.array(coords_list)[np.array(row_needed).flatten()]

            print(type(select_coords))
            ################

            #tsne = np.asarray(coords_list)
            tsne = select_coords
            df_Xtsne = pd.DataFrame(tsne)
            df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)

        '''
        # save the tSNE points to bve read later on irrespective of whoch option was chosen
        df_Xtsne_touse = pd.DataFrame(tsne)
        df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse.csv'), header=None, index=None)

        tx, ty = tsne[:,0], tsne[:,1]

        tx[tx ==0] = 0.2
        ty[ty ==0] = 0.1

        tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
        ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))

        ##identify the markers
        mm = np.asarray(LABELS_MARKERS)[chk_box_marker]

        tiles = []

        marker_choice = np.array(mc)[chk_box_marker]
        weight_choice = np.array(wc)[chk_box_marker]

        inds = range(len(marker_image_list))

        N = len(inds)

        for k in range(len(inds)):

            image_all_2 = []
            image_all_1 = []

            im = tifffile.imread(marker_image_list[inds[k]])
            if (rb_imtech_val ==2): #for codex
                im = im.reshape(64,5040,9408)

            for m in range(len(marker_choice)):
                image = im[marker_choice[m]]

                median_filtered = scipy.ndimage.median_filter(image, size=1)
                image_all_2.append(median_filtered)

                # apply threshold

                thresh = threshold_otsu(image_all_2[m])

                print('thresh is: ', str(thresh))

                bw = closing(image > thresh, square(1))

                # remove artifacts connected to image border
                cleared_imtsne = clear_border(bw)

                image_all_1.append(cleared_imtsne*weight_choice[m])

            tl = sum(image_all_1)

            if (rb_imtech_val ==0): #vectra
                tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))

            elif (rb_imtech_val ==1): #t-CyCIF
                tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
            elif (rb_imtech_val ==2): # CODEX
                 tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
            elif (rb_imtech_val ==3): # CyCIF
                 tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))

            old_size = tile_1.size

            if(rb_val==0):
                new_size = (old_size[0]+1, old_size[1]+1)
                new_im = Image.new("RGB", new_size,color='black')

            elif(rb_val==1): # for the border #response based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size,color_vec[inds[k]])## color='yellow')

            elif(rb_val==2): # for the border #treatment based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size,color_vec_tx[inds[k]])# color='yellow')

            elif(rb_val==3): # for the border #cluster based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size,color_vec_clasgn[inds[k]])# colours_58[cx])# color='yellow')

            elif(rb_val==4): # for the border #patient id  based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size, color_vec_patid[inds[k]])# color='yellow')

            new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))

            file_name = os.path.join(path_wd+'/output_tiles/image_tsne_tils'+str(k)+'.png')

            file_name_hover.append('image_tsne_tils'+str(k)+'.png')

            new_im.save(file_name)

            # Read Images

            tile_2 = Image.open(file_name)
            rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
            tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias

            full_image.paste(tile_2, (int((width-max_dim)*ty[k]), int((height-max_dim)*tx[k])),mask=tile_2.convert('RGBA'))

        matplotlib.pyplot.figure(figsize = (25,20))
        plt.imshow(full_image)
        plt.axis('off')

        k = random.randint(2,500)
        file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k)+'.png')
        full_image.save(file_name)

        rotated_img     = full_image.rotate(90)
        file_name_rot = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k)+'_rot.png')

        rotated_img.save(file_name_rot)

    ##code for reshuffling

    elif (rb_shf_val==1): # shuffle images

        #pick the tsne file to start with
        if(rb_rs_val==1):
            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), index_col=None, header= None)
            print('usertsne')
        elif(rb_rs_val==0):
            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
            print('origtsne')

        elif(rb_rs_val==2):
            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), index_col=None, header= None)
            print('seealltsne')

        tsne = np.array(df_Xtsne)

        # save the tSNE points to be read later on, irrespective of which option was chosen
        df_Xtsne_touse = pd.DataFrame(tsne)
        df_Xtsne_touse.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_touse.csv'), header=None, index=None)

        tx, ty = tsne[:,0], tsne[:,1]

        tx[tx ==0] = 0.2
        ty[ty ==0] = 0.1

        tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
        ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))

        full_image = Image.new('RGBA', (width, height))

        size = [256,256]

        ##identify the markers
        mm = np.asarray(LABELS_MARKERS)[chk_box_marker]

        tiles = []

        marker_choice = np.array(mc)[chk_box_marker]
        weight_choice = np.array(wc)[chk_box_marker]

        inds = range(len(marker_image_list))

        N = len(inds)
        shf_N = np.array(range(N))
        random.shuffle(shf_N)
        print("#############shuffle file order###")
        print(shf_N)
        count_file = 0

        for k in range(N):

            image_all_2 = []
            image_all_1 = []

            im = tifffile.imread(marker_image_list[shf_N[k]])
            if (rb_imtech_val ==2): #for codex
                im = im.reshape(64,5040,9408) ##

            for m in range(len(marker_choice)):
                image = im[marker_choice[m]]

                median_filtered = scipy.ndimage.median_filter(image, size=1)
                image_all_2.append(median_filtered)

                # apply threshold

                thresh = threshold_otsu(image_all_2[m])

                print(thresh)

                bw = closing(image > thresh, square(1))

                # remove artifacts connected to image border
                cleared_imtsne = clear_border(bw)

                image_all_1.append(cleared_imtsne*weight_choice[m])

            tl = sum(image_all_1)

            if (rb_imtech_val ==0): #vectra
                tile_1 = Image.fromarray(np.uint8(cm.viridis(tl)*255))

            elif (rb_imtech_val ==1): #t-CyCIF
                tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
            elif (rb_imtech_val ==2): # CODEX
                 tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))
            elif (rb_imtech_val ==3): # CyCIF
                 tile_1 = Image.fromarray(np.uint8(cm.jet(tl)*255))

            old_size = tile_1.size

            print(pat_ind_list[shf_N[k]])

            if(rb_val==0):
                new_size = (old_size[0]+1, old_size[1]+1)
                new_im = Image.new("RGB", new_size,color='black')

            elif(rb_val==1): # for the border #response based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size,color_vec[shf_N[k]])# color='yellow')

            elif(rb_val==2): # for the border #treatment based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size,color_vec_tx[shf_N[k]])# color='yellow')

            elif(rb_val==3): # for the border #cluster based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size,color_vec_clasgn[shf_N[k]])# color='yellow')

            elif(rb_val==4): # for the border #patient id  based
                new_size = (old_size[0]+50, old_size[1]+50)

                new_im = Image.new("RGB", new_size,color_vec_patid[shf_N[k]])# color='yellow')

            new_im.paste(tile_1, (int((new_size[0]-old_size[0])/2),int((new_size[1]-old_size[1])/2)))

            count_file = np.int(np.array(np.where(shf_N[k]==shf_N)).flatten())

            file_name = os.path.join(path_wd+'/output_tiles/image_tsne_tils'+str(shf_N[k])+'.png')

            file_name_hover.append('image_tsne_tils'+str(shf_N[k])+'.png')

            new_im.save(file_name)

            # Read Images

            tile_2 = Image.open(file_name)
            rs = max(1, tile_2.width/max_dim, tile_2.height/max_dim)
            tile_2 = tile_2.resize((int(tile_2.width/rs), int(tile_2.height/rs)), Image.ANTIALIAS) #commented antialias

            full_image.paste(tile_2, (int((width-max_dim)*ty[shf_N[k]]), int((height-max_dim)*tx[shf_N[k]])),mask=tile_2.convert('RGBA'))

        matplotlib.pyplot.figure(figsize = (25,20))
        plt.imshow(full_image)
        plt.axis('off')

        ## order filenamehover list before sending it out to the live panels
        so = np.argsort(shf_N)
        file_name_hover_np = np.array(file_name_hover)
        sorted_file_name_hover = file_name_hover_np[so]
        file_name_hover = sorted_file_name_hover.tolist()

        k1 = random.randint(2,500)
        file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k1)+'.png')

        full_image.save(file_name)

        rotated_img     = full_image.rotate(90)
        file_name_rot = file_name = os.path.join(path_wd+'/image_tSNE_GUI/static/image_tsne_tils_all_'+str(k1)+'_rot.png')

        rotated_img.save(file_name_rot)

    return([file_name_rot,tsne, file_name_hover])

###############################################################################
### button_callback()                                                      ####
### a) Calls the create_figure() that collects user inputs from the GUI    ####
### b) draw_tSNE_scatter() generates the tSNE plots for the live canvases  ####
### b) reads in the user-provided tSNE                                     ####
### c) generates thumbnails, and pastes these onto the static canvas       ####
### d) stores the thumbnails in the output folder                          ####
### e) updates the hover tool with thumbnail paths                         ####
###############################################################################

def button_callback():

    theme_t = theme_select.value
    if(theme_t == 'black'):
        curdoc().theme= theme_black
    elif(theme_t == 'gray'):
        curdoc().theme= theme_gray
    elif(theme_t == 'dark blue'):
        curdoc().theme= theme_blue

    jk = create_figure(stack_montage_flag)
    layout.children[1] = jk[0]

    print('############')
    print('In Button callback')
    tsne3 = jk[1]
    print(type(tsne3))
    print(tsne3)

    file_name_hover = jk[2]
    print('############fnh in button callback')
    print('file_name_hover')
    print(file_name_hover)

    markers_single = jk[3]
    print('############')
    print(type(markers_single))
    print(markers_single)

    cluster_ms_list = jk[4]
    print('############')
    print(type(cluster_ms_list ))
    print(cluster_ms_list )

    p1_out = draw_tSNE_scatter(tsne3, file_name_hover, cluster_ms_list)
    p1 = p1_out[0]
    p2 = p1_out[1]
    p3 = p1_out[2]
    p4 = p1_out[3]

    source = p1_out[4]

    tab1 = Panel(child=p1, title="Response")
    tab2 = Panel(child=p2, title="Treatment")
    tab3 = Panel(child=p3, title="Cluster Annotations")
    tab4 = Panel(child=p4, title="Patient id")

    tabs = Tabs(tabs=[ tab1, tab2, tab3, tab4 ])
    layout.children[2] = tabs

    return([p,p1,p2,p3,p4,source,tabs])

############################################################################
### create_figure() collects the user choices from the GUI and          ####
### calls either the generate_stack_montage() for reading in            ####
### a single image or the generate_image_tSNE() for multiplexed images  ####
############################################################################

def create_figure(stack_montage_flag):

    p = figure(tools=TOOLS,x_range=x_range, y_range=y_range,width=1000,height=1000)

    rb_imtech = radio_button_group_imtech.value

    rb = radio_button_group.value

    rb_rs = radio_button_group_RS.value

    rb_shf = radio_button_group_Shf.value

    chk_box_marker = checkbox_group.active

    print(chk_box_marker)

    chk_box_marker_sm = checkbox_group_sm.active
    print('**chk_box_marker_sm**')
    print(chk_box_marker_sm)

    if(rb=='No'):
        rb_val = 0
    elif(rb=='Based on Response'):
        rb_val = 1
    elif(rb=='Based on Treatment'):
        rb_val = 2
    elif(rb=='Based on Clusters'):
        rb_val = 3
    elif(rb=='Based on Patient id'):
        rb_val = 4

    if(rb_rs=='Generate random co-ordinates'):
        rb_rs_val = 1
    elif(rb_rs=='Use t-SNE co-ordinates'):
        rb_rs_val = 0
    elif(rb_rs == 'Arrange in rows'):
        rb_rs_val = 2

    if(rb_shf=='Yes'):
        rb_shf_val = 1
    else:
        rb_shf_val = 0

    if (rb_imtech == 'Vectra'):
        rb_imtech_val = 0
    elif (rb_imtech=='t-CyCIF'):
        rb_imtech_val = 1
    elif (rb_imtech=='CODEX'):
        rb_imtech_val = 2
    elif (rb_imtech=='CyCIF'):
        rb_imtech_val = 3

    ## adding in the codex, vectra, tcycif, cycif options
    # collecting the marker names for tcycif/codex stack montage

    cluster_ms_list = ['nil'] * len(LABELS_MARKERS)#[]
    clust_ms_list = []
    clust_ms_list_1 = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/markers.csv'),
                                         header= 0,index_col=None)
    markers_single = np.array(clust_ms_list_1.iloc[:,2]).flatten()

    ## need to update the markers_single array based on the order the files are read in
    if (stack_montage_flag==True):
        if (rb_imtech_val ==1): #SM for t-CyCIF data
            print('in SM for t-CyCIF')
            ms_list=[]
            cluster_ms_list = []
            for i in range(markers_single.shape[0]):
                print(i)
                print(pat_fov_list[i])
                print(markers_single)
                channel_num = np.int(pat_fov_list[i].split("_40X_")[1].split(".tiff")[0])
                ms_list.append(markers_single[channel_num-1])

            markers_single = np.copy(np.array(ms_list))

            # use this updated markers_single name order going forward
            for i in range(markers_single.shape[0]):

                cluster_ms_list.append('Channel '+ markers_single[i])

        elif (rb_imtech_val ==2): #SM for CODEX
            ## for codex sm for tonsils
            print('in SM for CODEX')
            ## need to update the markers_single array based on the order the files are read in
            ms_list=[]
            cluster_ms_list = []
            for i in range(markers_single.shape[0]):
                print(i)
                print(pat_fov_list[i])

                channel_name = pat_fov_list[i].split('.tif')[0].split('_')[3]
                channel_num = np.int(np.array(np.where(channel_name==markers_single)).flatten())
                ms_list.append(markers_single[channel_num])

            markers_single = np.copy(np.array(ms_list))

            # use this updated markers_single name order going forward
            for i in range(markers_single.shape[0]):

                cluster_ms_list.append('Channel '+ markers_single[i])

    elif (stack_montage_flag == False):

        # to handle all markers in t-CyCIF/CODEX/Vectra

        mc = []
        for m in range(len(LABELS_MARKERS)):
            in_mc = np.int(np.array(np.where(LABELS_MARKERS[m] == np.array(markers_single))).flatten())
            mc.append(in_mc)

        if (rb_imtech_val ==0):#Vectra
            wc = [100] * len(LABELS_MARKERS)
            wc[0] = 800
            wc[1]=wc[2] = 400
        elif (rb_imtech_val ==1):#t-CyCIF
            wc = [100] * len(LABELS_MARKERS)
            wc[0] = 50
        elif (rb_imtech_val ==2):
            wc = [150] * len(LABELS_MARKERS)
            wc[0] = 300 #
            wc[6] = 100

        elif (rb_imtech_val ==3):
            wc = [150] * len(LABELS_MARKERS)
            wc[1] = 100
            wc[2] = 300

        cluster_ms_list = ['nil'] * num_images #[]

    ###########

    if(len(chk_box_marker_sm)==0): #so we are using the multiple multiplexed option
        if(len(chk_box_marker)==0): #if no markers are chosen, display defaults

            file_name_1 = "image_tSNE_GUI/static/image_tsne_tils_all.png"
            p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])
            df_Xtsne = pd.read_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), index_col=None, header= None)
            df_Xtsne.shape
            tsne_points = np.array(df_Xtsne )
            file_name_hover = list(range(num_images))

        else:

            out1 = generate_image_tSNE(chk_box_marker, rb_val,rb_rs_val,rb_shf_val,rb_imtech_val, mc, wc, LABELS_MARKERS)
            file_name_all = out1[0]
            tsne_points = out1[1]
            file_name_hover = out1[2]

            print('file_name_hover after gen_image_tsne')
            print(file_name_hover)

            print('#########file_name_all########')
            print('#################')

            print(file_name_all)

            file_name_1 = file_name_all.split('/code')[1]
            print('#########file_name_1########')
            print(file_name_1)

            p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])

    elif(len(chk_box_marker_sm)==1):  #stack montage option
        print('chosen stack montage')
        out1sm = generate_stack_montage(chk_box_marker, rb_imtech_val, LABELS_MARKERS)
        file_name_all = out1sm[0]
        tsne_points = out1sm[1]
        file_name_hover = out1sm[2]

        print('############fnh post sm')
        print('file_name_hover')
        print(file_name_hover)

        print('#########file_name_all########')
        print('#################')

        print(file_name_all)

        file_name_1 = file_name_all.split('/code')[1]
        print('#########file_name_1########')
        print(file_name_1)

        p.image_url(url=[file_name_1], x=x_range[0],y=y_range[1],w=x_range[1]-x_range[0],h=y_range[1]-y_range[0])

    return ([p,tsne_points, file_name_hover,markers_single, cluster_ms_list])

##########
# define the colour vectors
colours_58 = ["firebrick","gold","royalblue","green","dimgray","orchid","darkviolet",
              "red", "orange", "limegreen", "blue", "purple", "seagreen","gold","darkolivegreen",
              "lightpink","thistle","mistyrose","saddlebrown","slategrey","powderblue",
            "palevioletred","mediumvioletred","yellowgreen","lemonchiffon","chocolate",
              "lightsalmon","lightcyan","lightblue", "darkorange","black","darkblue","darkgreen","paleturquoise","yellow","rosybrown",
             "steelblue","dodgerblue","darkkhaki","lime","coral","aquamarine","mediumpurple","violet","plum",
             "deeppink","navy","seagreen","teal","mediumspringgreen","cadetblue",
             "maroon","silver","sienna","crimson","slateblue","magenta","darkmagenta"]

colours_resp = ["yellow","red","green"]
colours_tx = ["orange","limegreen","violet"]

#####################################
#### Section that gets populated ####
####based on User uploads ###########
#####################################
#####################################
## This section reads in images, ####
## metadata, markers in the data,####
## and user choices              ####
#####################################
#####################################

path_wd = os.getcwd()
print('Current working directory: ')
print(path_wd)

### to generate tsne points from the onset itself

fname = os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv')
if os.path.isfile(fname):
    df_Xtsne = pd.read_csv(fname, index_col=None, header= None)
    tsne = np.array(df_Xtsne )
    tx, ty = tsne[:,0], tsne[:,1]
    tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
    ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))
    num_images = tsne.shape[0]

    ## to have the arrange_in_rows and random points already available
    # Choose up to k points around each reference point as candidates for a new
    # sample point
    k = 10

    # Minimum distance between samples
    r = 1.7

    width_1, height_1 = 20, 22

    print('Generating random co-ordinates')

    # Cell side length
    a = r/np.sqrt(2)
    # Number of cells in the x- and y-directions of the grid
    nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1

    # A list of coordinates in the grid of cells
    coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
    # Initilalize the dictionary of cells: each key is a cell's coordinates, the
    # corresponding value is the index of that cell's point's coordinates in the
    # samples list (or None if the cell is empty).
    cells = {coords: None for coords in coords_list}

    # Pick a random point to start with.
    pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
    samples = [pt]
    # Our first sample is indexed at 0 in the samples list...
    cells[get_cell_coords(pt,a)] = 0
    # ... and it is active, in the sense that we're going to look for more points
    # in its neighbourhood.
    active = [0]

    nsamples = 1
    # As long as there are points in the active list, keep trying to find samples.
    while (nsamples < num_images): #active:
        # choose a random "reference" point from the active list.
        idx = np.random.choice(active)
        refpt = samples[idx]
        # Try to pick a new point relative to the reference point.
        pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
        if pt:
            # Point pt is valid: add it to the samples list and mark it as active
            samples.append(pt)
            nsamples += 1
            active.append(len(samples)-1)
            cells[get_cell_coords(pt,a)] = len(samples) - 1
            print('nsamples is: ',str(nsamples))
        else:
            # We had to give up looking for valid points near refpt, so remove it
            # from the list of "active" points.
            active.remove(idx)

    tsne = np.asarray(samples)
    tx, ty = tsne[:,0], tsne[:,1]

    tx[tx ==0] = 0.2
    ty[ty ==0] = 0.1

    tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
    ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))

    df_Xtsne_m = pd.DataFrame(tsne)
    #df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), header=None, index=None)
    df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None) #14th march 2022

    ##### for 'arrange in rows' option at the onset itself

    # Minimum distance between samples
    r = 2

    width_1, height_1 = 20, 22

    print('Arrange images side-by-side')

    # Cell side length
    a = r/np.sqrt(2)
    # Number of cells in the x- and y-directions of the grid
    nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1

    # A list of coordinates in the grid of cells
    coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]

    #logic to get grid points - 29th March 2021
    m = np.int(np.floor(nx*ny/num_images))

    row_needed = []
    def multiples(m, num_images):
        for i in range(num_images):
            row_needed.append(i*m)

    multiples(m,num_images)

    select_coords = np.array(coords_list)[np.array(row_needed).flatten()]

    print(type(select_coords))

    tsne1 = select_coords

    tsne1[tsne1 ==0] = 0.2

    df_Xtsne = pd.DataFrame(tsne1)
    df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)

else:
    #read in images
    FoV_path = os.path.join(path_wd + '/user_inputs/figures/')
    count_images = 0
    for fname in os.listdir(FoV_path):
        print(fname)
        count_images = count_images + 1
    num_images = count_images
    #generate random 2D coords for these images

    ####
    #### tsne and clustering within mistic, if not provided by user
    ####

    im1 = tifffile.imread(os.path.join(FoV_path+fname))

    if (im1.shape[0] < 6):
        num_markers = im1.shape[0]
    else:
        num_markers = 6

    data_CM = np.zeros((1,num_markers)) # one row = one image with mean of markers in the columns, defaults to 6 markers

    for fname in os.listdir(FoV_path):
        print(fname)
        im1 = tifffile.imread(os.path.join(FoV_path+fname))
        if ((im1.shape[0]==16) and (im1.shape[1]==4) and (im1.shape[2]==5040) and (im1.shape[3]==9408)): #for codex intake
            im1 = im1.reshape(64,5040,9408)

        #if (rb_imtech_val ==2): #for codex
        #    im = im.reshape(64,5040,9408)

        image_summary_vec = np.zeros((1,num_markers))
        for m in range(num_markers):
                image1 = im1[m]

                median_filtered1 = scipy.ndimage.median_filter(image1, size=1)

                thresh = threshold_otsu(median_filtered1)

                bw = closing(image1 > thresh, square(1))

                # remove artifacts connected to image border
                cleared_imtsne = clear_border(bw)

                image_summary_vec[0,m] = np.mean(cleared_imtsne) #mean of each channel m per image fname

        data_CM = np.concatenate((data_CM,image_summary_vec),axis=0)

    data_CM = np.delete(data_CM, (0), axis=0)

    # perform tSNE
    tsne = TSNE(n_components=2, verbose=1)#, perplexity=30, n_iter=900)
    X_2d = tsne.fit_transform(data_CM)

    # Fit a Dirichlet process mixture of Gaussians using n components
    bgm = BayesianGaussianMixture(n_components=3, random_state=42).fit(data_CM)
    cluster_asgn = bgm.predict(data_CM)

    #save tSNE and cluster assignments

    df_Xtsne_u = pd.DataFrame(X_2d)
    df_Xtsne_u.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), header=None, index=None)

    fname = os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv')
    if (os.path.isfile(fname)==False):
        df_dpgmm_u = pd.DataFrame(cluster_asgn)
        df_dpgmm_u.to_csv(os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv'), header=None, index=None)

    ### end of tsne/clustering by mistic

    # Choose up to k points around each reference point as candidates for a new
    # sample point
    k = 10

    # Minimum distance between samples
    r = 1.7

    width_1, height_1 = 20, 22

    print('Generating random co-ordinates')

    # Cell side length
    a = r/np.sqrt(2)
    # Number of cells in the x- and y-directions of the grid
    nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1

    # A list of coordinates in the grid of cells
    coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]
    # Initilalize the dictionary of cells: each key is a cell's coordinates, the
    # corresponding value is the index of that cell's point's coordinates in the
    # samples list (or None if the cell is empty).
    cells = {coords: None for coords in coords_list}

    # Pick a random point to start with.
    pt = (np.random.uniform(0, width_1), np.random.uniform(0, height_1))
    samples = [pt]
    # Our first sample is indexed at 0 in the samples list...
    cells[get_cell_coords(pt,a)] = 0
    # ... and it is active, in the sense that we're going to look for more points
    # in its neighbourhood.
    active = [0]

    nsamples = 1
    # As long as there are points in the active list, keep trying to find samples.
    while (nsamples < num_images): #active:
        # choose a random "reference" point from the active list.
        idx = np.random.choice(active)
        refpt = samples[idx]
        # Try to pick a new point relative to the reference point.
        pt = get_point(k, refpt,r,a,nx,ny,cells,samples)
        if pt:
            # Point pt is valid: add it to the samples list and mark it as active
            samples.append(pt)
            nsamples += 1
            active.append(len(samples)-1)
            cells[get_cell_coords(pt,a)] = len(samples) - 1
            print('nsamples is: ',str(nsamples))
        else:
            # We had to give up looking for valid points near refpt, so remove it
            # from the list of "active" points.
            active.remove(idx)

    tsne = np.asarray(samples)
    tx, ty = tsne[:,0], tsne[:,1]

    tx[tx ==0] = 0.2
    ty[ty ==0] = 0.1

    tx = (tx-np.min(tx)) / (np.max(tx) - np.min(tx))
    ty = (ty-np.min(ty)) / (np.max(ty) - np.min(ty))

    df_Xtsne_m = pd.DataFrame(tsne)
    #df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE.csv'), header=None, index=None)
    df_Xtsne_m.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_user.csv'), header=None, index=None)

    ##### for 'arrange in rows' option at the onset itself

    # Minimum distance between samples
    r = 2

    width_1, height_1 = 20, 22

    print('Arrange images side-by-side')

    # Cell side length
    a = r/np.sqrt(2)
    # Number of cells in the x- and y-directions of the grid
    nx, ny = int(width_1 / a) + 1, int(height_1 / a) + 1

    # A list of coordinates in the grid of cells
    coords_list = [(ix, iy) for ix in range(nx) for iy in range(ny)]

    #logic to get grid points - 29th March 2021
    m = np.int(np.floor(nx*ny/num_images))

    row_needed = []
    def multiples(m, num_images):
        for i in range(num_images):
            row_needed.append(i*m)

    multiples(m,num_images)

    select_coords = np.array(coords_list)[np.array(row_needed).flatten()]

    print(type(select_coords))

    tsne1 = select_coords

    tsne1[tsne1 ==0] = 0.2

    df_Xtsne = pd.DataFrame(tsne1)
    df_Xtsne.to_csv(os.path.join(path_wd + '/user_inputs/metadata/X_imagetSNE_seeall.csv'), header=None, index=None)

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

# adding this for reading in data if no marker info is present
fname = os.path.join(path_wd + '/user_inputs/metadata/Marker_ids.csv')

if os.path.isfile(fname):
    df_markers = pd.read_csv(fname, index_col=None, header= None)
    markers_list = np.array(df_markers ).flatten()
    LABELS_MARKERS = []
    for j in range(markers_list.shape[0]):
        LABELS_MARKERS.append(markers_list[j])
    stack_montage_flag = False

else:
    LABELS_MARKERS = ['nil'] * 6
    stack_montage_flag = True

## read in images
marker_image_list = []
pat_fov_list = []
FoV_path = os.path.join(path_wd + '/user_inputs/figures/')
file_order = []

#sorted input
for fname in sorted(os.listdir(FoV_path)):
    print(fname)
    marker_image_list.append(FoV_path+fname)
    pat_fov_list.append(fname)

print("print image list")
print(marker_image_list)
print("file order")
print(file_order)

#################################
#################################
# checks for user metadata inputs
# if these files are not present, gray out the tSNE or points.

# collecting the response metadata

color_vec = []
resp_list= []
fname = os.path.join(path_wd + '/user_inputs/metadata/Response_categories.csv')

if os.path.isfile(fname):
    resp_list_1 = np.array(pd.read_csv(fname,header= None,index_col=None)).flatten()
    uni_resp,counts_resp = np.unique(resp_list_1,return_counts=True)

    for i in range(resp_list_1.shape[0]):
        row_t = np.int(np.array(np.where(resp_list_1[i]==uni_resp)).flatten())

        color_vec.append(colours_resp[row_t])

        resp_list.append(resp_list_1[i])
else:
    for i in range(num_images):
        color_vec.append('gray')
        resp_list.append('Response nil')

# collecting the treatment metadata

color_vec_tx = []
tx_list = []
fname = os.path.join(path_wd + '/user_inputs/metadata/Treatment_categories.csv')

if os.path.isfile(fname):
    tx_list_1= np.array(pd.read_csv(fname,header= None,index_col=None)).flatten()
    uni_tx,counts_tx = np.unique(tx_list_1,return_counts=True)
    for i in range(tx_list_1.shape[0]):
        row_t = np.int(np.array(np.where(tx_list_1[i]==uni_tx)).flatten())

        color_vec_tx.append(colours_tx[row_t])

        tx_list.append(tx_list_1[i])
else:
    for i in range(num_images):
        color_vec_tx.append('gray')
        tx_list.append('Treatment nil')

# collecting the cluster assignments metadata

fname = os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv')
color_vec_clasgn = []
cluster_anno_list = []
clust_asgn_list = []

if os.path.isfile(fname):
    clust_asgn_list_1 = np.array(pd.read_csv(fname, header= None,index_col=None)).flatten()
    for i in range(clust_asgn_list_1.shape[0]):

        color_vec_clasgn.append(colours_58[clust_asgn_list_1[i]])
        clust_asgn_list.append(clust_asgn_list_1[i])
        cluster_anno_list.append('Cluster '+ str(clust_asgn_list_1[i]))

else:
    #for bayesian clustering, if not present

    FoV_path = os.path.join(path_wd + '/user_inputs/figures/')

    #sorted input data add on 17th Feb 2022
    for fname in sorted(os.listdir(FoV_path)):
        print(fname)

    im1 = tifffile.imread(os.path.join(FoV_path+fname))

    if (im1.shape[0] < 6):
        num_markers = im1.shape[0]
    else:
        num_markers = 6

    data_CM = np.zeros((1,num_markers)) # one row = one image with mean of markers in the columns, defaults to 6 markers

    for fname1 in os.listdir(FoV_path):
        print(fname1)
        im1 = tifffile.imread(os.path.join(FoV_path+fname1))

        if ((im1.shape[0]==16) and (im1.shape[1]==4) and (im1.shape[2]==5040) and (im1.shape[3]==9408)): #for codex intake
            im1 = im1.reshape(64,5040,9408)

        image_summary_vec = np.zeros((1,num_markers))
        for m in range(num_markers):
                image1 = im1[m]

                median_filtered1 = scipy.ndimage.median_filter(image1, size=1)

                thresh = threshold_otsu(median_filtered1)

                bw = closing(image1 > thresh, square(1))

                # remove artifacts connected to image border
                cleared_imtsne = clear_border(bw)

                image_summary_vec[0,m] = np.mean(cleared_imtsne) #mean of each channel m per image fname

        data_CM = np.concatenate((data_CM,image_summary_vec),axis=0)

    data_CM = np.delete(data_CM, (0), axis=0)

    # Fit a Dirichlet process mixture of Gaussians using n components
    bgm = BayesianGaussianMixture(n_components=3, random_state=42).fit(data_CM)
    cluster_asgn = bgm.predict(data_CM)

    df_dpgmm_u = pd.DataFrame(cluster_asgn)
    df_dpgmm_u.to_csv(os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv'), header=None, index=None)

    fname2 = os.path.join(path_wd + '/user_inputs/metadata/Cluster_categories.csv')

    clust_asgn_list_2 = np.array(pd.read_csv(fname2, header= None,index_col=None)).flatten()
    for i in range(num_images):

        color_vec_clasgn.append(colours_58[clust_asgn_list_2[i]])
        clust_asgn_list.append(clust_asgn_list_2[i])
        cluster_anno_list.append('Cluster '+ str(clust_asgn_list_2[i]))

    ## bayes clustering end

# collecting the patient id metadata

fname = os.path.join(path_wd + '/user_inputs/metadata/Patient_ids.csv')
color_vec_patid = []
cluster_pat_list = []
clust_patid_list = []

if os.path.isfile(fname):

    # collecting the Patient id metadata

    clust_patid_list_1 = np.array(pd.read_csv(fname,header= None,index_col=None)).flatten()
    pat_ind_list = np.copy(clust_patid_list_1)

    for i in range(clust_patid_list_1.shape[0]):

        color_vec_patid.append(colours_58[clust_patid_list_1[i]])
        clust_patid_list.append(clust_patid_list_1[i])
        cluster_pat_list.append('Patient '+ str(clust_patid_list_1[i]))

else:
    pat_ind_list = []
    for i in range(num_images):
        color_vec_patid.append('gray')
        clust_patid_list.append('nil')
        cluster_pat_list.append('Patient nil')
        pat_ind_list.append('nil')

# generate dummy thumbnail names for hover panel

file_name_hover = []
file_name_hover_list = []

for i in range(num_images): #clust_patid_list_1.shape[0]):

    file_name_hover.append(str(i))
    file_name_hover_list.append('Thumbnail '+ str(i))

#################
# set up widgets
#################

RB_1 = ['Based on Response', 'Based on Treatment','Based on Clusters','Based on Patient id','No']

RB_2 = ['Generate random co-ordinates', 'Use t-SNE co-ordinates', 'Arrange in rows']

RB_3 = ['Yes', 'No']

RB_4 = ['Vectra', 't-CyCIF', 'CODEX', 'CyCIF']

TS_1 = ['black','gray', 'dark blue']

TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,save,box_select,"

x_range=(0,1)
y_range=(0,1)

#main image tSNE canvas
p = figure(tools=TOOLS,x_range=x_range, y_range=y_range,width=1000,height=1000)
tsne_points = np.zeros([1,2])

#additional tSNE scatter plot canvas

point_tSNE = figure(plot_width=350, plot_height=350,
              tools='hover,pan,wheel_zoom,box_select,reset')
point_tSNE.title.text = 'tSNE point cloud for Patient response'

point_tSNE.scatter(tsne[:,0],tsne[:,1],fill_alpha=0.6, color ='red',size=8,legend='Response')

point_tSNE.legend.location = "bottom_left"

theme_black = Theme(json={
    'attrs': {
        'Figure': {
            'background_fill_color': '#2F2F2F',
            'border_fill_color': '#2F2F2F',
            'outline_line_color': '#444444'
            },
        'Axis': {
            'axis_line_color': "white",
            'axis_label_text_color': "white",
            'major_label_text_color': "white",
            'major_tick_line_color': "white",
            'minor_tick_line_color': "white",
            'minor_tick_line_color': "white"
            },
        'Grid': {
            'grid_line_dash': [6, 4],
            'grid_line_alpha': .3
            },
        'Circle': {
            'fill_color': 'lightblue',
            'size': 10,
            },
        'Title': {
            'text_color': "white"
            }
        }
    })

theme_gray = Theme(json={
    'attrs': {
        'Figure': {
            'background_fill_color': '#555555',
            'border_fill_color': '#2F2F2F',
            'outline_line_color': '#444444'
            },
        'Axis': {
            'axis_line_color': "white",
            'axis_label_text_color': "white",
            'major_label_text_color': "white",
            'major_tick_line_color': "white",
            'minor_tick_line_color': "white",
            'minor_tick_line_color': "white"
            },
        'Grid': {
            'grid_line_dash': [6, 4],
            'grid_line_alpha': .3
            },
        'Circle': {
            'fill_color': 'lightblue',
            'size': 10,
            },
        'Title': {
            'text_color': "white"
            }
        }
    })

theme_blue = Theme(json={
    'attrs': {
        'Figure': {
            'background_fill_color': '#25256d',
            'border_fill_color': '#2F2F2F',
            'outline_line_color': '#444444'
            },
        'Axis': {
            'axis_line_color': "white",
            'axis_label_text_color': "white",
            'major_label_text_color': "white",
            'major_tick_line_color': "white",
            'minor_tick_line_color': "white",
            'minor_tick_line_color': "white"
            },
        'Grid': {
            'grid_line_dash': [6, 4],
            'grid_line_alpha': .3
            },
        'Circle': {
            'fill_color': 'lightblue',
            'size': 10,
            },
        'Title': {
            'text_color': "white"
            }
        }
    })

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

TOOLS="hover,pan,crosshair,wheel_zoom,zoom_in,zoom_out,box_zoom,undo,redo,reset,tap,save,box_select,poly_select,lasso_select,"

TOOLTIPS = [
        ("index", "$index"),
        ("(x,y)", "($x, $y)"),
        ("Pat_id", "@pat_list"),
        ("Response", "@res_list"),
        ("Treatment", "@tx_list"),
        ("Cluster id", "@clust_asgn_list"),
        ("Channel", "@cluster_ms_list"),
        ("Thumbnail", "@file_name_hover_list"),
        ("FoV","@fov_list")
    ]

p1 = figure(plot_width=400, plot_height=400, tooltips=TOOLTIPS,tools = TOOLS,
               title="Patient response")

#####################################################################################
## This block prepares and sets up the GUI layout, and collects user-input choices ##
#####################################################################################

desc = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/desc.html')).read(), sizing_mode="stretch_width")

desc_SM1 = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/descSM1.html')).read(), sizing_mode="stretch_width")

desc_SM = Div(text=open(os.path.join(path_wd + '/image_tSNE_GUI/descMontage.html')).read(), sizing_mode="stretch_width")

radio_button_group_imtech = Select(value='Vectra',
                          title='',
                          width=200,
                          options=RB_4)

radio_button_group = Select(value='No',
                          title='Image border',
                          width=200,
                          options=RB_1)

radio_button_group_RS = Select(value='Generate new co-ordinates',
                          title='tSNE co-ordinates',
                          width=220,
                          options=RB_2)

radio_button_group_Shf = Select(value='Yes',
                          title='Shuffle images',
                          width=200,
                          options=RB_3)

theme_select = Select(value = 'black',
                      title='Theme',
                      width = 200,
                      options = TS_1)

checkbox_group = CheckboxGroup(labels=LABELS_MARKERS)#, active=[0, 1])

button = Button(label='Run', width=100, button_type="success")

## added to activate Run button
button.on_click(button_callback)

## for stack montage
SM = ['Stack montage']
checkbox_group_sm = CheckboxGroup(labels=SM)

print('updating new p1#')

# set up layout and GUI refresh after every 'Run' click

out2 = create_figure(stack_montage_flag)
print(out2)
p = out2[0]
tsne2 = out2[1]
print('############')
print(type(tsne2))
print(tsne2)

file_name_hover = out2[2]
print('############fnh after create figure')
print(type(file_name_hover))
print(file_name_hover)

markers_single = out2[3]
print('############')
print(type(markers_single))
print(markers_single)

cluster_ms_list = out2[4]
print('############')
print(type(cluster_ms_list ))
print(cluster_ms_list )

p11_out = draw_tSNE_scatter(tsne2, file_name_hover,cluster_ms_list )

p1 = p11_out[0]
p2 = p11_out[1]
p3 = p11_out[2]
p4 = p11_out[3]

tab1 = Panel(child=p1, title="Response")
tab2 = Panel(child=p2, title="Treatment")
tab3 = Panel(child=p3, title="Cluster Annotations")
tab4 = Panel(child=p4, title="Patient id")

tabs = Tabs(tabs=[ tab1, tab2, tab3, tab4 ]) # (added tab4)

selects = column(desc,  radio_button_group_imtech, desc_SM1, checkbox_group_sm,  desc_SM,  checkbox_group,  radio_button_group, radio_button_group_RS, radio_button_group_Shf,  theme_select, button, width=520) #

layout=row(selects,p, tabs)#,selected_points)#create_figure())

#doc = curdoc()
curdoc().theme= theme_black

# add to document
curdoc().add_root(layout)

curdoc().title = "Mistic: Image tSNE viewer"

# cd image_tSNE_code/bokeh_GUI/bokeh-branch-2.3/examples/app
# bokeh serve --port 5098 --show image_tSNE_GUI


"""This program crawls the val-pakproducts site. It grabs the product
information, which can be seen in items.py.

It uses Scrapy and Selenium. We use selenium due to some js loaded data.

We use a Firefox webdriver for the selenium app.

HOW IT WORKS:
This is a CrawlSpider that starts at val-pakproducts.com/products, then
using the rules it goes to each product detail page on a given product list
page and grabs the relevant information and goes to the next page in
the product search results and repeats until all products have been scrapped.
"""

import time
import re
import logging as log
import itertools
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from scrapy import Selector
from valpak.items import ValpakScrapperItem
from scrapy.spiders import BaseSpider
from scrapy.http import Request, FormRequest
from scrapy.item import Item, Field

from selenium import webdriver
from selenium import selenium

class ValpakSpider(CrawlSpider):

    name = 'valpak'
    allowed_domains = ['val-pakproducts.com']
    #start_urls = ['http://val-pakproducts.com/products/?_sft_product_cat=miscellaneous-premier-parts']
    start_urls = ['http://val-pakproducts.com/products/']
    rules = (
        # Extract product links so that parse_items can scrap the appropriate data for each product.
        Rule(LinkExtractor(restrict_xpaths=('//div[@class="row"]/ul[@class="products"]//a[@class="main-link"]',)), callback='parse_items', follow=True),

        # Extract links so that the web crawler can move to the next page.
        Rule(LinkExtractor(restrict_xpaths=('//a[@class="next page-numbers"]',))),
    )

    def parse_items(self, response):
        """This gets the product info that we need from the products
        page. It calls _get_availability(), which selenium handles.
        """
        hxs = Selector(response)
        item = ValpakScrapperItem()
        #Get the fields we need
        try:
            item['title'] = hxs.xpath(
                '//h1[@class="product_title entry-title"]/text()'
            ).extract()[0]
        except KeyError:
            self.log('Unable to find title', level=log.WARNING)
        try:
            item['sku'] = hxs.xpath(
                '//span[@class="sku"]/text()'
            ).extract()[0]
            item['upc'] = item['sku'].replace('.','')
            self.log('Found sku', level=log.INFO)
        except KeyError:
            self.log('Unable to find sku', level=log.WARNING)
        try:
            item['category'] = hxs.xpath(
                '//span[@class="posted_in"]/a/text()'
            ).extract()[0]
        except KeyError:
            self.log('Unable to find category', level=log.WARNING)
        try:
            attributes = hxs.xpath(
                '//table[@class="shop_attributes"]'
            )
        except KeyError:
            self.log(
                'Unable to find table of attributes',
                level=log.WARNING
            )
        try:
            # We need to get the two large images and the thumbnails of the
            # images.
            images = hxs.xpath(
                '//div[contains(@class,"single-product-main-images")]//img/@src'
            ).extract()
            item['image_urls'] = images
        except KeyError:
            self.log(
                'Unable to find images',
                level=log.WARNING
            )
        print(item['sku'])
        self.log(item['sku'])
        #A dictionary that maps the website ids with the items.py ids.
        keymapper = {
            'original manufacturer':'og_manufacturer',
            'oem part number':'oem',
            "val-paks part number": 'part_number',
            "country of origin": "country"
        }
        for attribute in attributes.xpath('//tr'):
            keys = attribute.xpath('//th/text()').extract()
            values = attribute.xpath('//td/p/text()').extract()
            for key, value in itertools.izip(keys, values):
                normalized_key = key.replace("'","").lower()
                try:
                    item[keymapper[normalized_key]] = value
                except KeyError:
                    self.log(
                        'Unable to find %s %s' % (normalized_key, value),
                        level=log.WARNING
                    )
        try:
            part_number = self.get_part_number(item)
            item['part_number'] = part_number
        except:
            print("unable to get part_number...", item)
        try:
            upc = self.clean_upc(item['upc'])
            item['upc'] = upc
        except:
            print("Unable to clean upc...", item)
        try:
            sku = self.create_sku("Val-Pak", item['part_number'])
            item['sku'] = sku
        except:
            print("Unable to make sku...", item)
        try:
            print(item['sku'], item['part_number'])
        except:
            print("Unable to find either sku or part_number", item)
        return item

    def get_part_number(self, item):
        try:
            #val-pak specific: they have two part numbers for some products
            # one is the actual part number
            # and the other is has an S to specify a single uom for ordering.
            part_number = self.clean_part_number(item['part_number'])
        except:
            print("Part Number wasn't found. Using upc instead")
            part_number = self.clean_upc(item['upc'])
        return part_number

    def create_sku(self, manufacturer, part_number):
        return manufacturer[:3].upper() + "_" + part_number

    def clean_part_number(self, part_number):
        """ This is used to clean part numbers."""
        clean_part_number = part_number
        if ',' in part_number:
            value0, value1 = part_number.replace(" ", "").split(',')
            if not value0.endswith('S'):
                clean_part_number = value0
            else:
                clean_part_number = value1
        return clean_part_number

    def clean_upc(self, upc):
        return re.sub("[^0-9]", "", upc)


from typing import List

from originator import Originator

class Caretaker:
    def __init__(self, originator: Originator) -> None:
        self._originator = originator
        self._mementos: List[Originator.Memento] = []

    def backup(self) -> None:
        print('Caretaker: Saving Originator\'s state...')
        self._mementos.append(self._originator.save())

    def undo(self) -> None:
        if not self._mementos:
            return

        memento: Originator.Memento = self._mementos.pop()
        print(f'Caretaker: Restoring state to {memento.name}.')
        try:
            self._originator.restore(memento)
        except Exception as e:
            print(e)
            self.undo()

    def show_history(self) -> None:
        print('Caretaker: Here\'s the list of mementos.')
        for memento in self._mementos:
            print(memento.name)


import numpy as np
import tensorflow as tf
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
    try:
        # Currently, memory growth needs to be the same across GPUs
        for gpu in gpus:
            tf.config.experimental.set_memory_growth(gpu, True)
        logical_gpus = tf.config.experimental.list_logical_devices('GPU')
        print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs")
    except RuntimeError as e:
        # Memory growth must be set before GPUs have been initialized
        print(e)

from numpy import sin, cos, pi
from shapely.geometry import Polygon
from tensorflow import keras
from pyomo.gdp import *

import pickle
from tensorflow import keras
import tensorflow.keras.backend as kb

'''
Done:
1) Function to repair weights: outputs new parameters and nkeras model with new weights
2) Testing and verifcation of results, bug fixes and basic test cases for sanity checks.
3) Cost expression as input to weight_repair function.

In Progress:
1) Function to the gurobi for output constraints
2) Test for both the examples and do sanity checks
3) Write sampler for NN
4) Check for cost expression parsing and standardize the input, ouputs

Remaining:
1) Store in LP format
2) modification to repair weights (input architecture): tf and pytorch,
    future (h5py discussion)
3) Defined cost expression: MSE, SSE
4) Break model into two parts:

    1)   f(lin cons, model, samples)
            ...
            return solver_model, variable

    2) define additional constraints on solver_model and variables
    3) run wrapper, pyomo

'''

from matplotlib.pyplot import text
# from matplotlib import pyplot as plt
# plt.rcParams['text.usetex'] = True
import pickle
import matplotlib as mpl
import numpy as np

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_predict_train.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_orig = pickle.load(filehandle)

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_new_last_layer.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_4 = pickle.load(filehandle)

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_new_third_layer.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_3 = pickle.load(filehandle)

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_train.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_train = pickle.load(filehandle)

# def csa(y_pred_train_orig):
#     miss_samples_orig = np.sum(y_pred_train_orig[:,0]>0.5)
#     print(miss_samples_orig)
#     return (y_pred_train_orig.shape[0]-miss_samples_orig)/y_pred_train_orig.shape[0]

# csa_orig_train = csa(y_orig)
# csa_rep_4_train = csa(y_4)
# csa_rep_3_train = csa(y_3)

# print(csa_orig_train)
# print(csa_rep_4_train)
# print(csa_rep_3_train)

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_predict_train.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_orig = pickle.load(filehandle)

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_new_last_layer.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_4 = pickle.load(filehandle)

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_new_third_layer.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_3 = pickle.load(filehandle)

# with open('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/fk_data/y_train.data', 'rb') as filehandle:
#     # read the data as binary data stream
#     y_train = pickle.load(filehandle)

transform1 = np.array([[1, 0, 2.5], [0, 1, 2.5], [0, 0, 1]])  # transformation matrix 1
transform2 = np.array([[1, 0, -2.5], [0, 1, -2.5], [0, 0, 1]])  # transformation matrix 2
rotate = np.array([[cos(pi / 4), -sin(pi / 4), 0], [sin(pi / 4), cos(pi / 4), 0], [0, 0, 1]])  # rotation matrix
inp = np.array([[1.25, 3.75, 3.75, 1.25],[1.25, 1.25, 3.75, 3.75],[1, 1 , 1, 1]])
out = np.matmul(np.matmul(np.matmul(transform1, rotate), transform2), inp)
poly3 = Polygon([(out[0, 0], out[1, 0]), (out[0, 1], out[1, 1]), (out[0, 2], out[1, 2]), (out[0, 3], out[1, 3])])

# plt.plot(x_poly3, y_poly3, color='green', alpha=0.7, linewidth=3, solid_capstyle='round', zorder=2, label='Target Set')
# plt.legend(loc="upper left")
# plt.show()

model_orig = keras.models.load_model('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/affine_transform_model')
rep_model_3 = keras.models.load_model('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/repaired_affine_transform_layer3')
rep_model_2 = keras.models.load_model('/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/repaired_affine_transform_layer2')

with open("/home/local/ASUAD/tkhandai/nn_repair/NN-Repair/io_data_affine_transform.pickle", "rb") as data:
    x_train, y_train, x_test, y_test = pickle.load(data)

from shapely.geometry import Point
from shapely.geometry.polygon import Polygon

y_test_orig = model_orig.predict(x_test)
y_test_rep_3 = rep_model_3.predict(x_test)
y_test_rep_2 = rep_model_2.predict(x_test)

def csa(y_pred_train_orig):
    miss_samples_orig = 0
    for i in range(y_pred_train_orig.shape[0]):
        if not poly3.contains(Point(y_pred_train_orig[i,:])):
            miss_samples_orig = miss_samples_orig + 1
    # print(miss_samples_orig)
    return (y_pred_train_orig.shape[0]-miss_samples_orig)/y_pred_train_orig.shape[0]

csa_orig_train = csa(y_test_orig)
csa_rep_3_train = csa(y_test_rep_3)
csa_rep_2_train = csa(y_test_rep_2)

print(csa_orig_train)
print(csa_rep_3_train)
print(csa_rep_2_train)


import random

word_list = ["Hangman","apple","Balloon","Cat","Dog","Aeroplane","Addition","Xerophone"]
word = word_list[random.randint(0,4)]
word = word.lower()
temp_word = list("*" * len(word))

lives = 5
user_input = ''

print("Hang Man Game")
print("One Word Have Been Selected")
print("You have:" + str(lives) + "lives")

while list(temp_word) != list(word):
    if lives != 0:
        user_input = input("One character to get full words > ")
        if (len(user_input))  == 1:
            guess_word = user_input.lower()
            if guess_word in word:
                # temp_word = list(temp_word)
                # temp_word[word.index(guess_word)] = word[word.index(guess_word)]
                indices = [i for i, x in enumerate(word) if x == guess_word]
                for each in indices:
                    temp_word = list(temp_word)
                    temp_word[each] = word[each]
                temp_word = "".join(temp_word)
                print(temp_word)
                # temp_word = "".join(temp_word)
            else:
                lives = lives - 1
                print(lives)
        else:
            print("Please Put Only One Charcter")
    else:
        print("You lose!")
        show_word = "".join(word)
        print("The word is : " + show_word)
        break
else:
    print("Congratz,You Win!")


#!/usr/bin/env python3
import os
import pathlib
import shutil
import time

import fire
from PIL import Image

def get_creation_date_from_exif(path):
    img = Image.open(path)
    # 36867: DateTimeOriginal
    return img.getexif()[36867].split()[0].replace(":", "-")

def get_creation_date(path, exif=False):
    if exif:
        try:
            return get_creation_date_from_exif(path)
        except KeyError:
            return get_creation_date(path, exif=False)

    return time.strftime("%Y-%m-%d", time.gmtime(os.path.getctime(path)))

def main(source, destination, output_type="jpg", move=False, exif=False):
    """
    Import Pictures from card and save them in folders, one for each day

    Arguments:
        destination: str
            Destination folder
        output_type: str
            Picture type
        move: bool
            Move pictures instead of copying them
    """
    source = pathlib.Path(source)
    destination = pathlib.Path(destination)
    pics = [
        source / f
        for f in os.listdir(source)
        if f.lower().endswith(f".{output_type.lower()}")
    ]

    print(len(pics), "pics found")
    if not pics:
        return

    creation_date_per_pic = {pic: get_creation_date(pic, exif=exif) for pic in pics}
    dates = sorted(set(creation_date_per_pic.values()))

    for date in dates:
        save_dir = destination / date
        if os.path.exists(save_dir):
            print(f"Uh oh, path {save_dir} already existing")
        else:
            print("I will create a directory", save_dir)
            os.makedirs(save_dir)

    if move:
        print("Moving files...")
    else:
        print("Copying files...")

    for pic in pics:
        date_path = creation_date_per_pic[pic]
        dest_path = destination / date_path / output_type
        dest_path.mkdir(parents=True, exist_ok=True)
        dest_file = dest_path / pic.name

        if dest_file.exists():
            print(f"Uh oh, file {dest_file} already exists")
            continue

        if move:
            print(f"Moving file {pic} to {dest_file}")
            shutil.move(pic, dest_file)
        else:
            print(f"Copying file {pic} to {dest_file}".format(pic, dest_file))
            shutil.copy2(pic, dest_file)

def cli():
    fire.Fire(main)

if __name__ == "__main__":
    cli()


import client_modules
import communication_client
from crypto_switch import cryptography
import dataloader
from tqdm import tqdm
import numpy as np

if __name__ == "__main__":

    # establish connection
    comm = communication_client.ClientCommunication()
    comm.connect()
    print("Client: Connection established.")

    # create crypto
    crypto = cryptography.EncryptionUtils()
    comm.send(crypto.generate_keys())
    print("Client: Cryptography context created.")

    # load model
    model_description = comm.recv()
    model = client_modules.client_model_from_description(model_description, crypto, comm)
    print("Client: Model created from description successfully.")

    # load data
    _, test_data = dataloader.load_dataset("mnist")
    test_dataloader = dataloader.BatchLoader(test_data, 1, False)

    # test
    correct = 0
    total = 0
    for _ in tqdm(range(len(test_dataloader))):
        comm.send("test")
        inputs, labels = test_dataloader.get()
        inputs = inputs.numpy()
        labels = labels.numpy()
        x, x_another_share = client_modules.create_shares(inputs)
        comm.send(x_another_share)
        output = model.forward(x)
        output_another_share = comm.recv()
        output += output_another_share
        pred = np.argmax(output, axis=1)
        print(labels)
        print(pred)
    comm.send("finish")

    # close connection
    comm.close_connection()