from django.shortcuts import render
from django.http import Http404, JsonResponse
from django.http import HttpResponse
import base64
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.conf import settings
from django.template import  Context
# from tensorflow.python.keras.backend import set_session
# from keras.preprocessing.image import load_img
# from keras.preprocessing.image import img_to_array
# from keras.applications.imagenet_utils import decode_predictions
import matplotlib.pyplot as plt
from detection.settings import MEDIA_ROOT, MEDIA_URL
import numpy as np
# from keras.applications import vgg16
import datetime
import traceback
import requests
import cv2
import os
import json
import tempfile
import mimetypes

def index(request):
    if  request.method == "POST":
        f=request.FILES['sentFile'] # here you get the files needed
        max_age = request.POST['max_age']
        n_init = request.POST['n_init']
        response = {}
        file_name = "video.mp4"
        file_name_2 = default_storage.save(file_name, f)
        file_url = default_storage.url(file_name_2)
        # cap = cv2.VideoCapture(f)
        # frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
        file_video = file_name_2.split('.')[0]
        file_video_2 = MEDIA_ROOT+'/'+file_video+"_detecion.webm"
        dict = {
            "path": os.path.join(MEDIA_ROOT, file_name_2),
            "path_video": file_video_2,
            "max_age": max_age,
            "n_init": n_init,
        }

        req = requests.post("http://localhost:9080/predictions/my_model_name", data=dict)
        if req.status_code == 200:
            res = req.json()
        else:
            res = req.json()
        print(f"FILE VIDEO PATH::::{file_video_2}")

        with tempfile.TemporaryFile("w+t") as file:
            json.dump(res,file)
            response['name'] = res
            file_name = "result.json"
            file_name_2 = default_storage.save(file_name, file)
            file_url = default_storage.url(file_name_2)
            response['url'] = file_url

            return render(request,'download_file.html',{'filename': file_name_2,'videofile': file_video+"_detecion.webm"})
    else:
        return render(request,'homepage.html')

def download_file(request, filename='', videofile=''):
    if filename != '':
        url_video = "/"+MEDIA_URL+videofile
        # Define Django project base directory
        BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        # Define the full file path
        filepath = BASE_DIR + '/media/' + filename
        # Open the file for reading content
        path = open(filepath, 'rb')
        mime_type, _ = mimetypes.guess_type(filepath)
        # Set the return value of the HttpResponse
        response = HttpResponse(path, content_type=mime_type)
        if videofile!='':
            videopath = BASE_DIR + '/media/' + videofile
            path2 = open(videopath, 'rb')
            # Set the mime type
            mime_type2, _ = mimetypes.guess_type(videopath)
            # Set the return value of the HttpResponse
            response = HttpResponse(path2, content_type=mime_type2)
        # Set the HTTP header for sending to browser
        response['Content-Disposition'] = "attachment; filename=%s ; videofile=%s" % (filename, videofile)
        # Return the response value
        return response
    else:
        # Load the template
        print("FILE NAME:::::::: NO TIENE")

        return render(request, 'file.html')


# Question: https://leetcode.com/problems/single-threaded-cpu/
# Medium

from typing import Optional, List
from collections import defaultdict
from heapq import heappop, heappush

class Solution:
    def getOrder(self, tasks: List[List[int]]) -> List[int]:
        # maps time instant to the tasks which are available for processing at that time
        availability = defaultdict(list)
        # min heap that stores all the tasks that are available to process at this time
        available = []
        # hashset of all the time instants
        times = set()
        # output of the program
        order = []

        for idx, (enqueue_time, process_time) in enumerate(tasks):
            availability[enqueue_time].append((process_time, idx))
            times.add(enqueue_time)

        # we want all time instants in reverse sorted order
        # so we can compare current time with last element in this array
        # can can remove the last element in O(1) time
        times = sorted(list(times), reverse=True)
        # starting with the smallest time value
        time = times[~0]

        # loop runs till all the available tasks are not processed
        while len(times) != 0 or len(available) != 0:
            # if the current time is greater or equal to earliest time instant
            # in the times array
            # it will take all the available tasks of all time instants that are smaller than current time and add them to the min-heap
            while len(times) != 0 and time >= times[~0]:
                x = times.pop()
                for process_time, idx in availability[x]:
                    heappush(available, (process_time, idx))

            # it is possible that  all tasks are processed
            # which were available at or before current time instant
            # and that's why heap could be empty
            if len(available) != 0:
                process_time, idx = heappop(available)
                time += process_time
                order.append(idx)

            # if the heap is empty and the times array is not
            # that states the next available tasks will be at the last element of the times array
            elif len(times) != 0:
                time = times[~0]

        return order

# December 29, 2022

'''

# Kunal Wadhwa

'''


import numpy as np
import utils.display

def complex2rgb(complex_image, normalization=None):
    """
    Converts a complex image to a RGB image.
    :param complex_image: A 2D array
    :param normalization: An optional scalar to indicate the target magnitude of the maximum value (1.0 is saturation).
    :return:
    """
    A = np.abs(complex_image)
    P = np.angle(complex_image)

    if normalization:
        if normalization > 0:
            max_value = np.max(abs(A.ravel()))
            if max_value > 0:
                A *= (normalization / max_value)

    V = np.minimum(A, 1.0)

    H = P / (2 * np.pi) + 0.5
    S = np.ones(H.shape)

    hsv_image = np.concatenate((H[:, :, np.newaxis], S[:, :, np.newaxis], V[:, :, np.newaxis]), axis=2)

    return utils.display.hsv2rgb(hsv_image)  # Convert HSV to an RGB image


from fastapi import FastAPI
from .core.config import PROJECT_NAME
from mangum import Mangum

app = FastAPI(
    title=PROJECT_NAME,
    # if not custom domain
    # openapi_prefix="/Prod"
)

@app.get("/today")
def today():
    """
    Return today's schedule from the user's calendar.

    This path operation will:
    * return a map of events

    """
    return {"Today is a great day!"}

handler = Mangum(app, enable_lifespan=False)


import pandas as pd
import numpy as np
import tempfile
import shutil
import subprocess
import os
import re
import joint_gt_ceph as jgc
import seaborn as sns
import matplotlib.pyplot as plt
import itertools
import functools
from scipy import stats

# number of additional threads to bcftools; greater than 0 slowed down
# ThinkStation so much that the Debian desktop became unresponsive
#__addthreads__ = str(os.cpu_count() - 3)
__addthreads__ = '0'

def make_ts_aaf(mix='mix1', vartype='snp', region='chr22', overwrite=True,
        tsdir='/home/attila/projects/bsm/results/2019-03-18-truth-sets/chr22/snp/truthset'):
    '''
    Create AAF-based truth set partitions given genotype-based partitions and
    count variants.

    Parameters:
    mix: mix1, mix2 or mix3
    vartype: snp or indel
    region: chr22 or autosomes
    overwrite: True or False depending on wheter to force overwriting existing files
    tsdir: the path to the main directory in the hierarchy

    Returns:
    a pandas data frame whose rows are truth subsets at various AAFs and whose
    "nvariants" column contains the number of variants
    '''
    def helper(aaf):
        genotypes = gt_of_aaf[mix][aaf]
        invcfs = [indir + os.sep + g + '.vcf.gz' for g in genotypes]
        outvcf = mixdir + os.sep + str(aaf) + '.vcf.gz'
        unsorted_outvcf = mixdir + os.sep + str(aaf) + '-unsorted.vcf.gz'
        args0 = ['bcftools', 'concat', '-a', '--threads', __addthreads__, '-o', unsorted_outvcf, '-Oz'] + invcfs
        args1 = ['bcftools', 'sort', '-o', outvcf, '-Oz', unsorted_outvcf]
        args2 = ['bcftools', 'index', '--threads', __addthreads__, '-t', outvcf]
        if overwrite or not os.path.isfile(outvcf):
            subprocess.run(args0)
            subprocess.run(args1)
            os.remove(unsorted_outvcf)
            subprocess.run(args2)
        # count records
        args3 = ['bcftools', 'view', '--threads', __addthreads__, '-H', outvcf]
        args4 = ['wc', '-l']
        proc1 = subprocess.Popen(args3, shell=False, stdout=subprocess.PIPE)
        proc2 = subprocess.Popen(args4, shell=False, stdout=subprocess.PIPE, stdin=proc1.stdout)
        proc1.stdout.close()
        nrec = proc2.communicate()[0]
        nrec = int(nrec) # turn bytesliteral (e.g. b'5226\n') to integer
        return(nrec)
    gt_of_aaf = jgc.get_gt_of_aaf()
    indir = tsdir + os.sep + 'genotypes'
    aafdir = tsdir + os.sep + 'aaf'
    if not os.path.isdir(aafdir):
        os.mkdir(aafdir)
    unfdir = aafdir + os.sep + 'unfiltered'
    if not os.path.isdir(unfdir):
        os.mkdir(unfdir)
    mixdir = unfdir + os.sep + mix
    if not os.path.isdir(mixdir):
        os.mkdir(mixdir)
    aafs = gt_of_aaf[mix].keys()
    nrec = [helper(f) for f in aafs]
    res = pd.DataFrame({'region': region, 'vartype': vartype, 'sample': mix, 'AAF': list(aafs),
        'count': nrec})
    res = res.astype({'region': 'category', 'vartype': 'category', 'sample': 'category'})
    return(res)

def make_ts_aaf_get_nvariants(regions=['chr22', 'chr1_2', 'autosomes']):
    '''
    Calls make_ts_aaf for all combination of regions, vartypes and samples

    Parameters:
    regions: a list of genomic regions like chr22 or chr1_2

    Returns:
    a pandoc data frame with the number of variants at all those combinations and AAFs
    '''
    def tsaaf(mix, vartype, region):
        bdir = '/home/attila/projects/bsm/results/2019-03-18-truth-sets/'
        res = make_ts_aaf(mix=mix, vartype=vartype, region=region, tsdir=bdir + os.sep + region + os.sep + vartype + '/truthset')
        return(res)
    #regions = ['chr22','chr1_2', 'autosomes']
    vartypes = ['snp', 'indel']
    samples = ['mix1', 'mix2', 'mix3']
    nvariants = pd.concat([tsaaf(mix=m, vartype=v, region=r) for r in regions
        for v in vartypes for m in samples])
    nvariants = nvariants.astype({'region': 'category', 'vartype': 'category', 'sample': 'category'})
    return(nvariants)

def evaluate_model(Y, xset, model, only_params):
    '''
    Evaluate a model at a set of x values (AAF values)

    Parameters:
    Y: total number of somatic variants
    xset: the set of x values (AAF values) at which somatic variants exist in the truth set
    model: L1, L2,...
    only_params: return only model parameters a and b

    Returns
    if only_params=False: the set of y values (number of modeled somatic variants)
    if only_params=True: the model parameters a (intercept) and b (slope)
    '''
    if model == 'L1':
        x_max = 50
    elif model == 'L2':
        x_max = 100
    elif model == 'L3':
        x_max = 50
    elif model == 'L4':
        x_max = 100
    xsubset = [z for z in xset if z <= x_max]
    n = len(xsubset)
    xsum = sum(xsubset)
    a = Y / (n - xsum / x_max)
    b = - a / x_max
    if model in ['L3', 'L4']:
        a = Y / n
        b = 0
    if only_params:
        return({'a': a, 'b': b})
    def L_function(x):
        y = a + b * x
        return(y)
    yset = [L_function(z) for z in xsubset] + [0] * (len(xset) - n)
    return(yset)

def evalmodel2df(nvariants, sample, vartype, region, model, Y, p_som2germ=None):
    '''
    Calls evaluate_model for a combination of Y (total n.o. variants), sample,
    vartype, region, model

    Parameters:
    nvariants: a data frame returned by make_ts_aaf_get_nvariants
    Y: total number of somatic variants
    sample: mix1, mix2 or mix3
    vartype: snp or indel
    region: chr22 or autosomes
    model: L1, L2,...
    p_som2germ: -log_10 of the ratio of the number of somatic to germline variants

    Returns:
    a pandas data frame with the modeled number of variants
    '''
    xset = nvariants.loc[(nvariants['region'] == region) &
            (nvariants['sample'] == sample) &
            (nvariants['vartype'] == vartype), 'AAF']
    y = evaluate_model(Y=Y, xset=xset, model=model, only_params=False)
    df = pd.DataFrame({'y': y, 'x': xset, 'model': model, 'Y': int(Y),
        'p_som2germ': p_som2germ, 'sample': sample, 'vartype': vartype, 'region': region})
    return(df)

def combine_regions_germ_vars(regions={'autosomes': 2929051733, 'chr1_2': 249250621 + 243199373, 'chr22': 51304566},
        germ_vars = {'snp': 4e6, 'indel': 0.8e6}):
    '''
    Scales the number of germline variants according to the length of genomic
    regions

    Parameters:
    regions: a dictionary of lengths in bases for various regions
    germ_vars: the approximate expected number of germline snps and indels

    Returns:
    a pandoc data frame of approx number of germline variants at all combinations of region and variant type
    '''
    gk = germ_vars.keys()
    rk = regions.keys()
    res = {v: [int(germ_vars[v] * regions[r] / regions['autosomes']) for r in rk] for v in gk}
    res = pd.DataFrame(res, index=rk)
    return(res)

def evalmodel2df_all(nvariants, germ_vars=combine_regions_germ_vars()):
    '''
    Calls evalmodel2df for all combination of regions, vartypes samples,
    models and total n.o. variants

    Returns:
    a pandoc data frame with the number of modeled variants at all those combinations and AAFs
    '''
    regions = ['chr22', 'autosomes']
    vartypes = ['snp', 'indel']
    samples = ['mix1', 'mix2', 'mix3']
    models = ['L1', 'L2', 'L3', 'L4']
    p_som2germ = [2, 3, 4]
    l = [evalmodel2df(nvariants, sample=s, vartype=v, region=r, model=m,
        Y=germ_vars.at[r, v] * 10 ** (- s2g), p_som2germ=s2g)
            for s in samples for v in vartypes for r in regions for m in
            models for s2g in p_som2germ]
    return(pd.concat(l))

def get_taejeongs_aaf_sample(sample='S316', scale2pct=True):
    '''
    A single sample version of get_taejeongs_aaf; see details therein
    '''
    csv = '/big/data/bsm/Bae-2018-science/aan8690_TableS1/' + sample + '.csv'
    fr_cx = pd.read_csv(csv)['FR-CX']
    def helper(y):
        if isinstance(y, str) and re.search('somatic', y):
            def splitter(ix):
                return(np.int64(str(y).split(sep=':')[ix]))
            alt = splitter(-1)
            ref = splitter(-2)
            total = alt + ref
            return(alt / total)
        else:
            return(np.nan)
    aaf = [helper(y) for y in fr_cx]
    aaf = [y for y in aaf if not np.isnan(y)]
    if scale2pct:
        aaf = [100 * y for y in aaf]
    df = pd.DataFrame({'VAF': aaf, 'sample': sample})
    df = df.astype({'sample': 'category'})
    return(df)

def get_taejeongs_aaf(samples=['S316', 'S320'], scale2pct=True):
    '''
    Import Taejeong's VAF for all variants in a list of samples from his Science article

    See
    https://science.sciencemag.org/highwire/filestream/703017/field_highwire_adjunct_files/1/aan8690_TableS1.xlsx
    If multiple samples are given, a "pooled" sample is created from them.

    Parameters:
    samples: a list of samples (note that S275 fails to import)
    scale2pct: True if VAF should be in percent

    Returns:
    a pandas DataFrame with a column of VAF values and a sample column
    '''
    l = [get_taejeongs_aaf_sample(s, scale2pct=scale2pct) for s in samples]
    res = pd.concat(l)
    if len(samples) > 1:
        pooled = pd.DataFrame({'VAF': res['VAF'], 'sample': 'pooled'})
        res = pd.concat([res, pooled])
        res = res.astype({'sample': 'category'})
    #res = sum(l, [])
    return(res)

def lambda_hat(aaf):
    '''
    MLE of the rate parameter lambda from a sample of VAF

    Parameters:
    aaf: a list of VAF values

    Returns:
    MLE of lambda
    '''
    lhat = len(aaf) / sum(aaf)
    return(lhat)

def scaled_exponential1(lam, ntot):
    def expfun(aaf):
        aaf = np.array(aaf)
        y = lam * ntot * np.exp(- lam * aaf)
        return(y)
    return(expfun)

def scaled_exponential(lam, ntot, aafs):
    aafs = np.array(aafs) # ensure that aafs is a numpy array
    unscaled = lam * np.exp(- lam * aafs)
    scaled = ntot / sum(unscaled) * unscaled
    return(scaled)

def exp_model_df(nvariants, region, sample, vartype, s2g, lam):
    '''
    Create a histogram of AAF according to an exponential model

    Parameters:
    nvariants: the output of make_ts_aaf_get_nvariants, a pandas DataFrame
    region: 'autosomes' or 'chr22'
    sample: 'mix1', 'mix2', or 'mix3'
    vartype: 'snp' or 'indel'
    s2g: log base 10 of the odds of somatic : germline variants
    lam: rate lambda of the exponential

    Returns:
    a pandas DataFrame with AAF + count (i.e. histogram) and input parameters
    '''
    germ_vars = combine_regions_germ_vars()
    ntot = germ_vars[vartype][region] * 10 ** s2g
    ntot = np.int64(ntot)
    sel_rows = (nvariants['region'] == region) & (nvariants['sample'] ==
            sample) & (nvariants['vartype'] == vartype)
    aafs = nvariants.loc[sel_rows, 'AAF']
    count = scaled_exponential(lam, ntot, aafs)
    count = np.int64(count)
    d = {'AAF': aafs, 'count': count, 'region': region, 'sample': sample,
            'vartype': vartype, 's2g': s2g, 'ntot': ntot, 'lambda': lam}
    df = pd.DataFrame(d)
    df = df.astype({'region': 'category', 'sample': 'category', 'vartype': 'category'})
    return(df)

def exp_model_df_concat(nvariants, s2gs=[-2, -3, -4], lambdas=[0.2, 0.04]):
    '''
    Concatenate into a DataFrame the iterator created by exp_model_iter

    Parameters:
    it: the iterator created by exp_model_iter
    s2gs: a list of multiple levels of s2g (see the s2g parameter of exp_model_df)
    lambdas: a list multiple levels of lambda (see the lam parameter of exp_model_df)

    Returns:
    a pandas DataFrame similar to the value of exp_model_df
    '''
    categcols = ['region', 'sample', 'vartype']
    l = [nvariants[c].cat.categories for c in categcols]
    l = l + [s2gs] + [lambdas]
    it = itertools.product(*l)
    df = pd.concat([exp_model_df(nvariants, *y) for y in it])
    df = df.astype({'lambda': 'category', 's2g': 'category', 'region': 'category', 'sample': 'category', 'vartype': 'category'})
    return(df)

def exp_model_plot0(expm, s2g=-3, region='autosomes', onepanel=False):
    '''
    Plots a grid of histograms of AAF corresponding to a set of exponential models

    Parameters:
    expm: the value of exp_model_df_concat
    s2g: log base 10 of the odds of somatic : germline variants
    region: 'autosomes' or 'chr22'

    Returns:
    a seaborn FacetGrid object
    '''
    sns.set()
    sns.set_context('notebook')
    sel_rows = (expm['s2g'] == s2g) & (expm['region'] == region)
    if onepanel:
        sample = 'mix1'
        lam = 0.2
        sel_rows = sel_rows & (expm['lambda'] == lam) & (expm['sample'] == sample)
    df = expm.loc[sel_rows, :]
    if not onepanel:
        g = sns.FacetGrid(df, row='sample', col='lambda', hue='vartype', sharey=False, aspect=2, margin_titles=True)
    else:
        g = sns.FacetGrid(df, hue='vartype', sharey=False, aspect=1.5)
    g = (g.map(plt.plot, 'AAF', 'count', marker='o', linestyle='dotted',
            markeredgecolor='white').set_axis_labels('VAF', 'count'))
    g = g.add_legend()
    return(g)

def exp_model_plot1(expm, sample='mix1', region='autosomes'):
    sns.set()
    sns.set_context('notebook')
    sel_rows = (expm['sample'] == sample) & (expm['region'] == region)
    df = expm.loc[sel_rows, :]
    g = sns.FacetGrid(df, row='lambda', col='s2g', hue='vartype',
            sharey=False, margin_titles=True)
    g.map(plt.plot, 'AAF', 'count', marker='o', linestyle='dotted', markeredgecolor='white')
    g = g.add_legend()
    return(g)

def aaf_distplot(aafdf=get_taejeongs_aaf(), fit=stats.expon, onesample=None):
    '''
    Plot the distribution of VAF values

    If the input aafdf contains multiple samples then a multi-plot grid is
    created.

    Parameters:
    aafdf: the pandas DataFrame output of get_taejeongs_aaf
    fit: None or a scipy.stats distribution

    Returns: a seaborn FacetGrid object
    '''
    sns.set()
    sns.set_context('notebook')
    if onesample is None:
        g =  sns.FacetGrid(aafdf, row='sample', aspect=2.5)
    elif onesample in set(aafdf['sample']):
        g =  sns.FacetGrid(aafdf.loc[aafdf['sample'] == onesample, :], aspect=1.5)
    else:
        raise ValueError('"onesample" must be None or one of the samples in "aafdf"')
    #g =  sns.FacetGrid(aafdf, row='sample', aspect=2.5, height=4)
    if fit is None:
        ylab = 'count'
    else:
        ylab = 'normalized count'
    g = (g.map(sns.distplot, 'VAF', hist=True, rug=True, kde=False,
            fit=fit).set_axis_labels('VAF', ylab))
    return(g)

def aaf_distplot1(aafdf):
    samples = aafdf['sample'].cat.categories
    nsamples = len(samples)
    fig, ax = plt.subplots(nsamples, 1)
    def plot_sample(sample):
        ix = samples.searchsorted(sample)
        n, bins, patches = ax[ix].hist(aafdf[aafdf['sample'] == sample]['VAF'])
        res = pd.DataFrame({'count': list(n) + [list(n)[-1]], 'bins': bins,
            'sample': sample})
        ax[ix].set_title(sample)
        return(res)
    histo = [plot_sample(s) for s in samples]
    histo = pd.concat(histo)
    g = sns.FacetGrid(histo, row='sample')
    g.map(plt.step, 'bins', 'count', where='post')
    return(histo)

def nvar_aaf_plot(nvariants, region='autosomes', sample=None):
    sns.set()
    sns.set_context('notebook')
    sel_rows = (nvariants['region'] == region)
    if sample is None:
        fg = sns.FacetGrid(data=nvariants.loc[sel_rows, :], row='sample', hue='vartype', sharey=False, aspect=2)
    elif sample in ['mix1', 'mix2', 'mix3']:
        sel_rows = sel_rows & (nvariants['sample'] == sample)
        fg = sns.FacetGrid(data=nvariants.loc[sel_rows, :], hue='vartype', sharey=False, aspect=1.5)
    fg = (fg.map(plt.plot, 'AAF', 'count', marker='o',
        markeredgecolor='white', linestyle='dashed').set_axis_labels('VAF', 'count'))
    fg = fg.add_legend()
    return(fg)

def downsample_aaf_vcf(ssize, invcfpath, outvcfpath, seed=19760415):
    '''
    Downsample one VCF

    Parameters:
    ssize: the number of calls in the sample (sample size)
    invcfpath: the path to the unfiltered input VCF
    outvcfpath: the path to the filtered output VCF
    seed: for sampling

    Returns:
    the regions (positions) of the sampled calls in a pandas DataFrame
    '''
    args0 = ['bcftools', 'view', '--threads', __addthreads__, '-H', invcfpath]
    args1 = ['cut', '-f1,2']
    proc0 = subprocess.Popen(args0, shell=False, stdout=subprocess.PIPE)
    proc1 = subprocess.Popen(args1, shell=False, stdout=subprocess.PIPE, stdin=proc0.stdout)
    regions = pd.read_csv(proc1.stdout, sep='\t', names=['CHROM', 'POS'], dtype={'CHROM': 'category', 'POS': np.int64})
    outs, errs = proc1.communicate()
    # downsampling regions
    sample = regions.sample(n=ssize, replace=False, axis=0, random_state=seed)
    sample = sample.sort_index()
    discarded_ix = set(regions.index) - set(sample.index)
    discarded = regions.loc[discarded_ix, :]
    discarded = discarded.sort_index()
    # output: regions file
    outdirpath = os.path.dirname(outvcfpath)
    outbname = os.path.basename(outvcfpath)
    discarded_vcfpath = outdirpath + os.sep + 'discarded-' + outbname
    if not os.path.exists(outdirpath):
        os.makedirs(outdirpath)
    if ssize == 0:
        shutil.copyfile(invcfpath, discarded_vcfpath)
        subprocess.run(['bcftools', 'index', '--tbi', discarded_vcfpath])
    else:
        regionspath = outvcfpath + '.regions'
        sample.to_csv(regionspath, sep='\t', header=False, index=False)
        # create reduced VCF
        args2 = ['bcftools', 'view', '--threads', __addthreads__, '-R', regionspath, '-Oz', invcfpath]
        args3 = ['bcftools', 'norm', '--rm-dup', 'both', '-Oz', '-o', outvcfpath]
        proc2 = subprocess.Popen(args2, shell=False, stdout=subprocess.PIPE)
        proc3 = subprocess.run(args3, shell=False, stdout=subprocess.PIPE, stdin=proc2.stdout)
        args4 = ['bcftools', 'index', '--tbi', outvcfpath]
        subprocess.run(args4)
        os.remove(regionspath)
        # create VCF from discarded records
        tempdir = tempfile.TemporaryDirectory()
        tempVCF = tempdir.name + os.path.sep + '0000.vcf.gz'
        args5 = ['bcftools', 'isec', '-C', '-Oz', '-p', tempdir.name, invcfpath, outvcfpath]
        subprocess.run(args5)
        shutil.move(tempVCF, discarded_vcfpath)
        shutil.move(tempVCF + '.tbi', discarded_vcfpath + '.tbi')
    return(sample)

def deduce_pathname(expm, topdir='/home/attila/projects/bsm/results/2019-03-18-truth-sets'):
    ix0 = expm.index[0]
    region = expm.at[ix0, 'region']
    vartype = expm.at[ix0, 'vartype']
    lam = expm.at[ix0, 'lambda']
    s2g = expm.at[ix0, 's2g']
    sample = expm.at[ix0, 'sample']
    basedir = topdir + os.path.sep + region + os.path.sep + vartype + os.path.sep + 'truthset/aaf/'
    indir = basedir + 'unfiltered/' + sample + os.path.sep
    outdir = basedir + 'exp_model/lambda_' + str(lam) + os.path.sep + 's2g_' + str(s2g) + os.path.sep + sample + os.path.sep
    d = {'indir': indir, 'outdir': outdir}
    return(d)

def downsample_all_aaf_vcfs(expm, topdir='/home/attila/projects/bsm/results/2019-03-18-truth-sets', seed=19760415):
    '''
    Downsample all VCFs referred to in expm

    Parameters:
    expm: a pandas DataFrame
    topdir: new directories and VCF-s will be created under it
    seed: for sampling

    Returns:
    the regions (positions) of the sampled calls in a dictionary of pandas
    DataFrames, where each key is an AAF
    '''
    d = deduce_pathname(expm=expm, topdir=topdir)
    indir = d['indir']
    outdir = d['outdir']
    outvcf = outdir + 'complete.vcf.gz'
    discarded_vcf = outdir + 'discarded-complete.vcf.gz'
    def helper(ix):
        '''
        Downsample a single VCF identified by ix of the pandas Data Frame expm
        '''
        aaf = expm.at[ix, 'AAF']
        ssize = expm.at[ix, 'count']
        invcfpath = indir + str(aaf) + '.vcf.gz'
        outvcfpath = outdir + str(aaf) + '.vcf.gz'
        discarded_vcfpath = outdir + 'discarded-' + str(aaf) + '.vcf.gz'
        df = downsample_aaf_vcf(ssize, invcfpath, outvcfpath, seed=seed)
        outpath = {'outvcfpath': outvcfpath, 'discarded_vcfpath': discarded_vcfpath}
        return(outpath)
    vcflist = [helper(ix) for ix in expm.index]
    outvcflist = [y['outvcfpath'] for y in vcflist if
            os.path.isfile(y['outvcfpath'])]
    discarded_vcflist = [y['discarded_vcfpath'] for y in vcflist]
    concat_vcfs(outvcf=outvcf, invcfs=outvcflist)
    concat_vcfs(outvcf=discarded_vcf, invcfs=discarded_vcflist)
    return(outvcf)

def concat_vcfs(outvcf, invcfs):
    '''
    Concatenate a list of VCFs into outvcf then sort and index that

    Parameters:
    outvcf: path to the output VCF
    invcfs: list of paths to the input VCFs

    Returns: None
    '''
    if os.path.exists(outvcf):
        os.remove(outvcf)
    # concatenate input VCFs and sort
    args0 = ['bcftools', 'concat', '-a', '--threads', __addthreads__] + invcfs
    args1 = ['bcftools', 'sort', '-Oz', '-o', outvcf]
    proc0 = subprocess.Popen(args0, shell=False, stdout=subprocess.PIPE)
    proc1 = subprocess.run(args1, shell=False, stdout=subprocess.PIPE,
            stdin=proc0.stdout)
    # make index
    args2 = ['bcftools', 'index', '-f', '--threads', __addthreads__, '-t', outvcf]
    subprocess.run(args2)
    return(None)

def split_up_expm(expm):
    '''
    Splits up expm according to the combination of the levels of categorical
    columns

    Parameters:
    expm: the output of make_ts_aaf_get_nvariants or exp_model_df

    Returns:
    the content of expm split into a list of DataFrames
    '''
    categcols = [c for c in expm.columns if expm[c].dtype.name == 'category']
    def helper(t):
        zip_l = list(zip(categcols, t))
        bool_l = [expm[y[0]] == y[1] for y in zip_l]
        sel_rows = functools.reduce(lambda a, b: a & b, bool_l)
        #sel_rows = bool_accumulate(bool_l)
        df = expm.loc[sel_rows, :]
        return(df)
    l = list(itertools.product(*[expm[c].cat.categories for c in categcols]))
    res = [helper(y) for y in l]
    return(res)

def downsample_absolutely_all_vcfs(expm, topdir='/home/attila/projects/bsm/results/2019-03-18-truth-sets', seed=19760415):
    '''
    Create truthset VCFs for all combinations of parameters respecting the directory tree created by make_ts_aaf

    Parameters:
    expm: a pandas DataFrame
    topdir: new directories and VCFs will be created under it
    seed: for sampling

    Returns:
    the pathnames of output VCFs
    '''
    l = split_up_expm(expm)
    l = [y for y in l if len(y) > 0]
    outvcfs = [downsample_all_aaf_vcfs(df, topdir=topdir, seed=seed) for df in l]
    return(outvcfs)

def prec_recall_absolutely_all_vcfs(expm, topdir='/home/attila/projects/bsm/results/2019-03-18-truth-sets', seed=19760415):
    '''
    TODO
    '''
    l = split_up_expm(expm)
    l = l[0:2] # for testing
    outdirs = [deduce_pathname(expm=y, topdir=topdir) for y in l]
    return(outdirs)
    #prec-recall-vcf -t $truthsetvcf $outdir/*.vcf.gz > $prdir/prec-recall.csv
    truthsetvcf
    args = ['prec-recall-vcf', '-t', outvcf]
    subprocess.run(args)
    pass

def bool_accumulate(bool_l):
    '''
    Currently this function has a semantic bug.
    '''
    end = bool_l.pop()
    front = bool_l.copy()
    if len(front) == 1:
        val = front[0] & end
        return(val)
    else:
        return(bool_accumulate(front))


""" Group Input into Captures """
import numpy as np
import os
import argparse
from datetime import datetime
import logging

from pre_processing.utils import (
    image_check_stats, read_image_inventory,
    export_inventory_to_csv, update_time_checks)
from config.cfg import cfg
from utils.logger import set_logging

flags = cfg['pre_processing_flags']

# args = dict()
# args['inventory'] = '/home/packerc/will5448/data/pre_processing_tests/ENO_S1_inventory.csv'
# args['output_csv'] = '/home/packerc/will5448/data/pre_processing_tests/ENO_S1_captures_TEST.csv'
# args['log_dir'] = '/home/packerc/will5448/data/pre_processing_tests/'
# args['no_older_than_year'] = 2017
# args['no_newer_than_year'] = 2019

def create_new_image_path(image_data):
    """ Create new image path """
    image_dir = os.path.dirname(image_data['image_path_original'])
    new_name = create_new_image_name(image_data)
    return os.path.join(image_dir, new_name)

def create_new_image_path_rel(image_data):
    """ Create new image path """
    image_dir = os.path.dirname(image_data['image_path_original_rel'])
    new_name = create_new_image_name(image_data)
    return os.path.join(image_dir, new_name)

def create_new_image_name(image_data):
    """ Create a new image new based on image_data:
        - PLN_S1_A01_R1_IMAG0001.JPG
    """
    season = image_data['season']
    site = image_data['site']
    roll = 'R{}'.format(image_data['roll'])
    image_no_rank = 'IMAG{:04}'.format(image_data['image_rank_in_roll'])
    ending = '.{}'.format(image_data['image_name_original'].split('.')[1])
    new_name = '_'.join([season, site, roll, image_no_rank])
    return ''.join([new_name, ending])

def group_images_into_site_and_roll(inventory):
    # Group images into site and roll
    site_roll_inventory = dict()
    for image_id, image_data in inventory.items():
        # if image_data['datetime'] == '':
        #     continue
        season = image_data['season']
        site = image_data['site']
        roll = image_data['roll']
        season_site_roll_key = '#'.join([season, site, roll])
        if season_site_roll_key not in site_roll_inventory:
            site_roll_inventory[season_site_roll_key] = dict()
        site_roll_inventory[season_site_roll_key][image_id] = image_data
    return site_roll_inventory

def calculate_time_deltas(inventory, flags):
    """ Calulate time deltas between subsequent images """
    # group images into site and roll
    site_roll_inventory = group_images_into_site_and_roll(inventory)
    image_time_deltas = dict()
    # iterate over each roll individually
    for season_site_roll_key, site_roll_data in site_roll_inventory.items():
        # get all images from the current site and roll
        times = list()
        paths = list()
        # iterate over each image in a roll
        for image_id, image_data in site_roll_data.items():
            img_time = image_data['datetime']
            times.append(img_time)
            paths.append(image_id)
        # Define the order of the images by 1) time and 2) by name
        datetimes = [
            datetime.strptime(
                x,
                flags['time_formats']['output_datetime_format'])
            for x in times]
        times_seconds = [
            (x-datetime(1970, 1, 1)).total_seconds()
            for x in datetimes]
        ordered_indexes = np.lexsort((paths, times_seconds))
        # Re-order by the newly defined order
        paths_ordered = [paths[i] for i in ordered_indexes]
        times_seconds_ordered = [times_seconds[i] for i in ordered_indexes]
        # Calculate time deltas between subsequent images
        # (next and previous) in seconds and days
        delta_seconds_next_ordered = [
            (times_seconds_ordered[i+1] - times_seconds_ordered[i])
            for i in range(0, len(times_seconds_ordered)-1)]
        delta_days_next_ordered = [
            '{:.2f}'.format((x / (60*60*24)))
            for x in delta_seconds_next_ordered]
        delta_days_next_ordered.append(0)
        delta_seconds_next_ordered.append(0)
        # calculate times to next image
        delta_seconds_last_ordered = [
            abs(times_seconds_ordered[i] - times_seconds_ordered[i-1])
            for i in range(1, len(times_seconds_ordered))]
        delta_days_last_ordered = [
            '{:.2f}'.format((x / (60*60*24)))
            for x in delta_seconds_last_ordered]
        delta_days_last_ordered.insert(0, 0)
        delta_seconds_last_ordered.insert(0, 0)
        # loop over all deltas
        # (starting with the delta between first and second)
        image_rank_in_roll_ordered = [1]
        for i, delta in enumerate(delta_seconds_last_ordered):
            image_rank_in_roll_ordered.append(i+2)
        # add information to inventory
        for i in range(0, len(paths_ordered)):
            image_time_deltas[paths_ordered[i]] = {
                'image_rank_in_roll': image_rank_in_roll_ordered[i],
                'seconds_to_next_image_taken': delta_seconds_next_ordered[i],
                'seconds_to_last_image_taken': delta_seconds_last_ordered[i],
                'days_to_last_image_taken': delta_days_last_ordered[i],
                'days_to_next_image_taken': delta_days_next_ordered[i]}
    return image_time_deltas

# Group images into captures - based on timestamps
def group_images_into_captures(inventory, flags):
    """ Group images into capture events by time deltas
        Output: dict
            - key unique id for an image
            - values: {'capture': 1, 'image_rank_in_capture': 1}
    """
    site_roll_inventory = group_images_into_site_and_roll(inventory)
    image_to_capture = dict()
    for season_site_roll_key, site_roll_data in site_roll_inventory.items():
        # order images by time (rank in roll)
        image_to_order = {
            k: v['image_rank_in_roll']
            for k, v in site_roll_data.items()}
        images_sorted = sorted(image_to_order.items(), key=lambda x: x[1])
        # initialize captures and ranks
        capture_ids_ordered = [1]
        image_rank_in_capture_ordered = [1]
        current_capture = 1
        current_rank_in_capture = 1
        # loop over all deltas
        # (starting with the delta between first and second)
        for i, (img_name, rank) in enumerate(images_sorted[1:]):
            delta = float(inventory[img_name]['seconds_to_last_image_taken'])
            max_delta = flags['image_check_parameters']['capture_delta_max_seconds']
            if delta <= max_delta:
                current_rank_in_capture += 1
            else:
                current_rank_in_capture = 1
                current_capture += 1
            capture_ids_ordered.append(current_capture)
            image_rank_in_capture_ordered.append(current_rank_in_capture)
        for i, (image_name, rank) in enumerate(images_sorted):
            image_to_capture[image_name] = {
                'capture': capture_ids_ordered[i],
                'image_rank_in_capture': image_rank_in_capture_ordered[i]
            }
    return image_to_capture

def update_inventory_with_capture_data(inventory, image_to_capture):
    """ update inventory with capture data """
    # merge capture info with inventory
    # and determine new image names
    for image_path_original, image_capture_data in image_to_capture.items():
        # add capture information
        inventory[image_path_original].update(image_capture_data)

def update_inventory_with_capture_id(inventory):
    """ add /update capture_id to each image"""
    for image_id in inventory.keys():
        image_data = inventory[image_id]
        capture_id = '{season}#{site}#{roll}#{capture}'.format(**image_data)
        image_data.update({'capture_id': capture_id})

def update_inventory_with_image_names(inventory):
    """ Create new image names """
    for image_data in inventory.values():
        image_data['image_path'] = \
            create_new_image_path(image_data)
        image_data['image_name'] = \
            create_new_image_name(image_data)
        image_data['image_path_rel'] = \
            create_new_image_path_rel(image_data)

def update_time_checks_inventory(inventory, flags):
    """ Update time check flags in inventory """
    for image_id, image_data in inventory.items():
        try:
            update_time_checks(image_data, flags)
        except:
            pass

if __name__ == '__main__':

    # Parse command line arguments
    parser = argparse.ArgumentParser()
    parser.add_argument("--inventory", type=str, required=True)
    parser.add_argument("--output_csv", type=str, required=True)
    parser.add_argument("--no_older_than_year", type=int, default=1970)
    parser.add_argument("--no_newer_than_year", type=int, default=9999)
    parser.add_argument("--log_dir", type=str, default=None)
    parser.add_argument(
        "--log_filename", type=str, default='group_inventory_into_captures')
    args = vars(parser.parse_args())

    # image check parameters
    msg_width = 99

    # logging
    set_logging(args['log_dir'], args['log_filename'])

    logger = logging.getLogger(__name__)

    # update time checks
    time_checks = flags['image_check_parameters']
    time_checks['time_too_old']['min_year'] = args['no_older_than_year']
    time_checks['time_too_new']['max_year'] = args['no_newer_than_year']

    inventory = read_image_inventory(
        args['inventory'],
        unique_id='image_path_original')

    # calculate time_deltas
    time_deltas = calculate_time_deltas(inventory, flags)
    update_inventory_with_capture_data(inventory, time_deltas)

    # group images into captures
    image_to_capture = group_images_into_captures(inventory, flags)
    update_inventory_with_capture_data(inventory, image_to_capture)

    update_inventory_with_capture_id(inventory)

    update_inventory_with_image_names(inventory)

    update_time_checks_inventory(inventory, flags)

    image_check_stats(inventory)

    first_cols_to_export = [
        'capture_id', 'season', 'site', 'roll', 'capture',
        'image_rank_in_capture', 'image_rank_in_roll',
        'image_name', 'image_path_rel', 'image_path',
        'datetime', 'seconds_to_next_image_taken',
        'seconds_to_last_image_taken', 'days_to_last_image_taken',
        'days_to_next_image_taken',
        'image_name_original', 'image_path_original',
        'image_path_original_rel']

    export_inventory_to_csv(
            inventory,
            args['output_csv'],
            first_cols=first_cols_to_export)


#coding:utf-8

'''
Created on 2016/02/25

@author: misato
'''

import matplotlib.pyplot as plt

def box_plot(data_list, label_list, y_axis_name, chance_level, _title = ""):
    """
    箱ひげ図のプロット
    @param data_list: データのリスト <2次元list n labels * m samples>
    @param label_list: 横軸のラベル名のリスト  <1次元list n labels>
    @param y_axis_name:  縦軸の名前
    @param chance_level: チャンスレベル
    @keyword _title: タイトル
    """
    # 図
    fig = plt.figure()
    subfig = fig.add_subplot(111)
    subfig.set_title(_title)

    # データのセット
    subfig.boxplot(data_list)
    # 横軸のラベルのセット
    subfig.set_xticklabels(label_list, rotation=45, fontsize = 8)
    # 縦軸の設定
    plt.ylabel(y_axis_name)
    plt.ylim([0.0,1.0])
    # グリッド表示
    plt.grid()
    # チャンスレベル設定
#    plt.abline(a = [0, chance_level], b = [3, chance_level])
#    plt.abline(h=chance_level)

    # 表示
    plt.show()

if __name__=="__main__":
    data_list = [[0.1,0.2,0.3,0.4,0.5],[0.2,0.3,0.4,0.5,0.6]]
    label_list = ["a", "b"]
    y_axis_name = "test"
    chance_level = 3

    box_plot(data_list, label_list, y_axis_name, chance_level, _title="a")


import sqlite3
import asyncio
import aiohttp
import webhook

conn = sqlite3.connect('xx.db', isolation_level=None, check_same_thread=False)
curs = conn.cursor()

async def claim(session, url, gid, token, **kwargs):
    resp = await session.request('GET', url=f'https://api.vk.com/method/utils.checkScreenName?screen_name={url}&access_token={token}&v=5.131', **kwargs)
    data = await resp.json()
    print(data)
    try:
        if data['response']['status'] == 1:
            resp = await session.request('GET', url=f'https://api.vk.com/method/groups.edit?group_id={gid}&screen_name={url}&access_token={token}&v=5.131', **kwargs)
            data = await resp.json()
            print(data)
            if data['response'] == 1:
                curs.execute('DELETE FROM `claimlist` WHERE `url` = ?', (url,))
                webhook.embed('Autoclaimed URL', f'Successfully autoclaimed {url}', 3066993)
                webhook.message(f'{url}: {gid}, {token}')
                return data
    except:
        if data['error']['error_code'] == 5:
            with open('tokens.txt', 'r') as file:
                lines = file.readlines()
            with open('tokens.txt', 'w') as file:
                for line in lines:
                    if line.strip('\n') != f'{token}':
                        file.write(line)
            conn.execute('DELETE FROM `claimlist` WHERE `token` = ?', (token,))
            return data

async def main(**kwargs):
    while True:
        async with aiohttp.ClientSession() as session:
            tasks = []
            for url in conn.execute("SELECT * FROM `claimlist`").fetchall():
                await asyncio.sleep(0.2)
                tasks.append(claim(session=session, url=url[0], gid=url[1], token=url[2], **kwargs))
            await asyncio.gather(*tasks, return_exceptions=True)

asyncio.ensure_future(main())

loop = asyncio.get_event_loop()
loop.run_forever()


#! python3

#----------------------------------------------------------------------------
# Author:           LJT
#
# Import Name:      Basic
# Purpose:          API for external
#
# Func Summary:     htmlSoup(url, session, headers, encoding='UTF-8')
#----------------------------------------------------------------------------

from bs4 import BeautifulSoup
import requests
import traceback

def htmlSoup(url, session, headers, encoding='UTF-8'):
    try:
        html = session.get(url, headers=headers)
        html.raise_for_status()
        html.encoding = encoding
        return BeautifulSoup(html.content, 'html.parser')
    except:
        return ""


import json
from itertools import combinations
import bisect
from collections import defaultdict
import numpy as np
import time
import sys
import random
from trie import Trie

cur_list = dict() # current items present in the frequent itemset
itemset = list() # map of item to its index (if required)
sum_freq=0.0
max_count=1.0

def read_items(): #list of all items

	global itemset
	with open ("BMS/bms1_items","r") as infile:
		itemset = infile.readlines()
	for i in range(len(itemset)):
		itemset[i] = itemset[i].rstrip()

	itemset.sort()

class FrequentItemSet(object):

	def __init__(self, ipfile, window_size = 60, decay = 0.95, prune_norm = 3):

		self.window_size = window_size
		self.decay_rate = decay
		self.norm = prune_norm
		self.wstart = 0
		self.wend = window_size-1
		self.max_iset_size = 3
		self.inputfile = ipfile
		self.trie = Trie(decay)
		self._init_trans()

	'''
	def _init_trans(self): # THIS IS ONLY FOR ONLINE RETAIL
		#Parse the json file and create a dict
		data = dict()
		with open(self.inputfile,'r') as infile:
			data = json.load(infile)
		infile.close()
		print ('Total No of transactions: '+str(len(data)))
		trans_list = list()
		for i in range(len(data)):
			trans_list.append(list(map(str,data[i]['Items'])))

		self.trans_list = trans_list
	'''

	def _init_trans(self):  # THIS IS ONLY FOR BMS
		#Parse bms data files and create dict
		trans_list = list()
		with open(self.inputfile, 'r') as infile:
			for line in infile:
				temp = line.split(" ")
				temp = temp[0:len(temp)-1]
				trans_list.append(temp)

		self.trans_list = trans_list

	def _get_code(self,iset):#returns a code for very set

		indices = list()
		for item in iset:
			indices.append(bisect.bisect_left(itemset, item))

		return indices

	def _get_iset(self,indices):

		iset = list()
		for index in indices:
			iset.append(itemset[index])

		return iset

	def process_window(self):
		global max_count, sum_freq
		trans_window = self.trans_list[self.wstart:self.wend+1] # extract the current window
		print ('Window length-'+str(len(trans_window))+' start - '+str(self.wstart)+' end- '+str(self.wend))
		trans_no = self.wstart
		for transaction in trans_window:
			unique_transaction=set(transaction)
			trans_no+=1
			if len(unique_transaction) > 120:
				continue
			if len(cur_list) > 1500000:
				print ('ALERT: Memory consumption high!')
				sys.exit(0)

	def _get_average(self):
		global sum_freq
		sum=pow((sum_freq/len(cur_list)),(1/self.norm))
		return sum

if __name__ == "__main__":

	start_time = time.time()
	''' Pruning done at the end of one window '''
	read_items()
	#print (itemset)
	#fis = FreqItemSet("trans_data.json", 90, 0.95, 2.5)
	fis = FreqItemSet("BMS/bms1_data", 90, 0.95, 2)
	count=0;
	while fis.wstart<(len(fis.trans_list)):
		print ('count of window - '+str(count))
		fis.process_window()
		while len(cur_list)>70000:
			temp=len(cur_list)
			fis.prune(fis._get_average(), 10)
			if temp==len(cur_list):
				break
		count+=1
		#if count%5==0:
		fis.show_topk()

	fis.show_topk()
	print("--- %s seconds ---" % (time.time() - start_time))


#!/usr/bin/env python3

import os
import sys
import csv
import glob
import time
import serial
import qdarkstyle
import numpy as np
import subprocess as sp

from PySide2.QtWidgets import *
from PySide2 import QtGui
from PySide2 import QtCore
from image import ImageWindow, CenterPointWindow, FocusWindow
from SimpleFNN import FNN
import torch
import gphoto2 as gp

class MainWindow(QWidget):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        # Set a few fonts to be used
        self.small_text = QtGui.QFont()
        self.small_text.setFamily('Helvetica')
        self.small_text.setPointSize(15)

        self.large_text = QtGui.QFont()
        self.large_text.setFamily('Helvetica')
        self.large_text.setPointSize(30)

        # Group section counter
        self.section_count = 0
        self.section_start = 0
        self.started_sects = False

        # Set trackers for what sections have been taken
        self.section_a = False
        self.section_b = False
        self.section_c = False

        # Indicator for if the image has to be taken again
        self.retake = False

        # Image counter
        self.image_count = None

        self.trim_first = True
        self.sect_first = True

        self.model = self.init_cnn_model()

        # Create the arduino connection
        # Try all three USB ports
        # global arduino
        # try:
        #     arduino = serial.Serial('/dev/ttyACM0', 9600)     #dev/cu.usbmodem14201
        # except IOError:
        #     try:
        #         arduino = serial.Serial('/dev/cu.usbmodem14301', 9600)
        #     except IOError:
        #         try:
        #             arduino = serial.Serial('/dev/cu.usbmodem14401', 9600)
        #         except IOError:
        #             print("You don't have the right arduino port.")
        #             sys.exit()
        # Set main window properties
        self.setGeometry(300, 300, 1200, 800)
        self.setWindowTitle('Slice-n-Snap')

        # Call the functions for initialzing the layouts
        directoryLayout = self._initDirsLayout()
        tableLayout = self._initTableLayout()
        hitLayout = self._initHitLayout()

        # Add the initialized layouts to the main layout
        mainLayout = QGridLayout()
        mainLayout.addLayout(directoryLayout, 0, 0)
        mainLayout.addLayout(tableLayout, 1, 0)
        mainLayout.addLayout(hitLayout, 2, 0)

        # Set the layout
        self.setLayout(mainLayout)

    def selectDirectory(self):
        '''Function for selecting an existing directory'''
        return QFileDialog.getExistingDirectory(self)

    def image_dir_buttonClick(self):
        # Have the user select the directory for the images
        self.image_dir = self.selectDirectory()
        # Update the label with the directory they chose
        self.image_dir_label.setText(self.image_dir + '/')
        # Disable the button after the directory has been chosen
        self.image_dir_button.setDisabled(True)
        # Start monitoring the directory
        # self._startWatcher()

        # Do I need to have a file list on this object?
        # if not self.backup_dir_button.isEnabled():
            # self.main_table.setDisabled(False)
            # self.hit_button.setDisabled(False)
            # self.iso_button_surface.setDisabled(False)
            # self.fstop_button_surface.setDisabled(False)
            # self.ss_button_surface.setDisabled(False)
            # msg = "Everything Nominal"
            # self.main_label.setText(msg)
        self.backup_dir_button.setEnabled(True)
        # Populate the table
        self.populateTable()
            # self.calibrationImage()

    def backup_dir_buttonClick(self):
        # Have the user select the directory
        self.backup_dir = self.selectDirectory()
        # Update the label with the directory they chose
        self.backup_dir_label.setText(self.backup_dir + '/')
        # Disable the button so they can't change the directory
        self.backup_dir_button.setDisabled(True)
        # Need to check that the backup directory is the same as the image directory
        self.backup()

        if not os.path.exists(self.backup_dir + '/csv_files/'):
            os.makedirs(self.backup_dir + '/csv_files/')

        # Only if both directories have been selected do we enable the rest of the GUI
        # if not self.image_dir_button.isEnabled():
        self.main_table.setDisabled(False)
        self.trim_button.setDisabled(False)
        self.sect_button.setDisabled(False)
        self.iso_button_surface.setDisabled(False)
        self.fstop_button_surface.setDisabled(False)
        self.ss_button_surface.setDisabled(False)
        self.iso_button_scatter.setDisabled(False)
        self.fstop_button_scatter.setDisabled(False)
        self.ss_button_scatter.setDisabled(False)
        self.capture_button.setDisabled(False)
        self.main_button.setDisabled(False)
        temp = "Everything Nominal"
        self.main_label.setText(temp)
            # Populate the table
            # self.populateTable()
            # self.calibrationImage()

    def backup(self):
        '''Function for backing up the image directory'''
        # Create a list of the image files is the main and backup dirs
        image_list = glob.glob(self.image_dir + '/*.nef')
        backup_list = glob.glob(self.backup_dir + '/*.nef')

        # Add the JPEGs
        image_list += glob.glob(self.image_dir + '/*.jpg')
        backup_list += glob.glob(self.backup_dir + '/*.jpg')

        # Create a list of CSV files
        csv_main_list = glob.glob(self.image_dir + '/csv_files/*')
        csv_backup_list = glob.glob(self.backup_dir + '/csv_files/*')

        # Remove any duplicates
        image_names = [x.split('/')[-1] for x in image_list]
        backup_names = [x.split('/')[-1] for x in backup_list]

        csv_main_names = [x.split('/')[-1] for x in csv_main_list]
        csv_backup_names = [x.split('/')[-1] for x in csv_backup_list]

        # Make a list of images to copy
        copy_names = [x for x in image_names if x not in backup_names]
        csv_names = [x for x in csv_main_names if x not in csv_backup_names]

        # Copy the images to the backup directoy
        for image in copy_names:
            sp.Popen(["cp", self.image_dir + '/' + image, self.backup_dir])

        for file in csv_names:
            sp.Popen(["cp", self.image_dir + '/csv_files/' +
                      file, self.backup_dir + '/csv_files/'])
        # Do we need to wait here?

    def trim_buttonClick(self):
        print(self.trim_first)
        self.sect_button.setEnabled(False)

        # check if distance is changed if first click
        if (self.trim_first == True):
            self.trim_first = False
            # print(self.main_table.rowCount())
            print(self.total_distance, int(self.main_table.item(self.main_table.rowCount()-1,1).text()))
            if (self.total_distance != int(self.main_table.item(self.main_table.rowCount()-1,1).text())):
                reply = QMessageBox.question(self, 'Total Distance Not Match', 'Are you sure you want to change the total distance?',
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

                if reply == QMessageBox.Yes:
                    self.total_distance = int(self.main_table.item(self.main_table.rowCount()-1,1).text())
                    # temp = "Everything Nominal"
                    # self.main_label.setText(temp)
                else:
                    temp = "Please correct the Distance"
                    self.main_label.setText(temp)
                    self.trim_first = True
                    # self.trim_button.setEnabled(False)
                    # self.sect_button.setEnabled(False)
                    # self.capture_button.setEnabled(False)
                    return
        temp = "Everything Nominal"
        self.main_label.setText(temp)

        self.image_distance = self.image_distance + 10
        self.total_distance = self.total_distance + 10
        self.image_dist_label.setText('Image Distance: ' + str(self.image_distance) + ' μm')
        self.total_dist_label.setText('Total Distance: ' + str(self.total_distance) + ' μm')

        if self.image_distance == 50:
            msg = "Take An Image!"
            self.trim_button.setText(msg)
            self.trim_button.setStyleSheet("color: red")
            self.sect_button.setText(msg)
            self.sect_button.setStyleSheet("color: red")
            self.main_table.setEnabled(False)
            self.trim_button.setEnabled(False)
            self.sect_button.setEnabled(False)

    def sect_buttonClick(self):

        print(self.sect_first)
        self.trim_button.setEnabled(False)

        # check if distance is changed if first click
        if (self.sect_first == True):
            self.sect_first = False
            # print(self.main_table.rowCount())
            print(self.total_distance, int(self.main_table.item(self.main_table.rowCount()-1,1).text()))
            if (self.total_distance != int(self.main_table.item(self.main_table.rowCount()-1,1).text())):
                reply = QMessageBox.question(self, 'Total Distance Not Match', 'Are you sure you want to change the total distance?',
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

                if reply == QMessageBox.Yes:
                    self.total_distance = int(self.main_table.item(self.main_table.rowCount()-1,1).text())
                    # print('Total distanc changed')
                else:
                    temp = "Please correct the Distance"
                    self.main_label.setText(temp)
                    self.sect_first = True
                    # self.trim_button.setEnabled(False)
                    # self.sect_button.setEnabled(False)
                    # self.capture_button.setEnabled(False)
                    return
        temp = "Everything Nominal"
        self.main_label.setText(temp)

        self.image_distance = self.image_distance + 5
        self.total_distance = self.total_distance + 5
        self.image_dist_label.setText('Image Distance: ' + str(self.image_distance) + ' μm')
        self.total_dist_label.setText('Total Distance: ' + str(self.total_distance) + ' μm')
        self.sect_first = False

        if self.image_distance == 50:
            msg = "Take An Image!"
            self.trim_button.setText(msg)
            self.trim_button.setStyleSheet("color: red")
            self.sect_button.setText(msg)
            self.sect_button.setStyleSheet("color: red")
            self.main_table.setEnabled(False)
            self.trim_button.setEnabled(False)
            self.sect_button.setEnabled(False)

    def populateTable(self):

        # Check for CSV files
        self.csv_list = sorted(glob.glob(self.image_dir + '/csv_files/*.csv'))

        # If the directory is empty then make a folder
        if self.csv_list == []:
            if not os.path.exists(self.image_dir + '/csv_files/'):
                try:
                    os.makedirs(self.image_dir + '/csv_files/')
                except FileExistsError:
                    pass
            # Take away all of the rows
            self.main_table.setRowCount(0)
            self.main_table.setColumnCount(7)

            # initiate the table_list as empty
            self.table_list = []

        else:
            # Open the last saved csv file
            with open(self.csv_list[-1], 'r') as f:
                reader = csv.reader(f, delimiter=',')
                self.table_list = []
                for row in reader:
                    self.table_list.append(row)

            # Get the shape of the table and set the table to reflect that
            shape = np.shape(self.table_list)
            self.main_table.setRowCount(shape[0])
            self.main_table.setColumnCount(shape[1])

            # Update the total_distance
            self.total_distance = int(self.table_list[-1][1])
            self.total_dist_label.setText('Total Distance: ' + str(self.total_distance) + ' μm')

            # Try and get the most recent secntion counter
            section_list = []
            total_distance = []
            for i in range(0, len(self.table_list)):
                if self.table_list[i][2] == 'None':
                    pass
                else:
                    section_list += [self.table_list[i][2]]
                    total_distance += [self.table_list[i][1]]

            if section_list == []:
                self.section_count = 0
                self.section_start = 0
            else:
                self.started_sects = True
                # Format: *a, *b, *c. There is no guarantee the sectioning is less than 10
                last_section_number = int(section_list[-1].split(';')[0][:-1])        # Modified, remove the last letter.
                self.section_start = int(total_distance[0]) - 50
                # print(self.total_distance - self.section_start, self.started_sects)
                # print(last_section_number)
                # quit()
                # After each load, check if need message sectioning
                if (self.total_distance - self.section_start) % 250 == 0:
                    msg = "You should be taking sections"
                    self.main_label.setText(msg)

            # Iterate through the items in the list and set the appropriate table items
            for i, row in enumerate(self.table_list):
                for j, col in enumerate(row):
                    item = QTableWidgetItem(col)
                    self.main_table.setItem(i, j, item)

    def save(self):
        path = self.image_dir + '/csv_files/'
        # Append the image name to the csv - will make it different
        # name = 'csv_depth_{0}_'.format(str(self.total_distance).zfill(4))

        if (type(self.table_list[-1][0]) == type("string")):
            table_list_1_name = str(self.table_list[-1][0][2:18])

        else:
            table_list_1_name = str(self.table_list[-1][0][0])

        name = "csv_" + table_list_1_name + ".csv"
        print(name)

        # Each time we call save(), using the data in main_table. time ok?
        save_table_list = self.table_list.copy()

        for i, row in enumerate(self.table_list):
            for j, col in enumerate(row):
                save_table_list[i][j] = self.main_table.item(i,j).text()

        with open(path + name, mode='w') as f:
            writer = csv.writer(f, delimiter=',')
            for row in save_table_list:
                writer.writerow(row)

        # Backup to make sure the CSV file gets copied
        self.backup()

    def closeEvent(self, event):
        self.save()
        event.accept()
        # reply = QMessageBox.question(self, 'Window Close', 'Are you sure you want to close the window?',
        #         QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        # if reply == QMessageBox.Yes:
        #     event.accept()
        #     print('Window closed')
        # else:
        #     event.ignore()

    # def save_amendment_and_exit(self):
    #     # change & save before capture
    #     # print(self.table_list[-1][0], type(self.table_list[-1][0]))
    #     if (type(self.table_list[-1][0] == type("string"))):
    #         table_list_1_name = []
    #         table_list_1_name.append(self.table_list[-1][0][2:18])
    #         # print(table_list_1_name)
    #     else:
    #         table_list_1_name = self.table_list[-1][0]

    #     for i, row in enumerate(self.table_list):
    #         for j, col in enumerate(row):
    #             self.table_list[i][j] = self.main_table.item(i,j).text()

    #     # check here if the talbe_list is a string

    #     self.table_list[-1][0] = table_list_1_name
    #     # print(self.table_list[-1][0])
    #     # print(self.table_list[-1][0][0])
    #     self.save()

    #     self.close()
    #     self.destroy()

    # Open a window for user to provide the center info of the tissure
    def centerCapture(self):
        self.setEnabled(False)
        self.center_window = CenterPointWindow(self.image_dir, self.backup_dir, self)

        self.center_window.show()
        app.processEvents()
        self.center_window.getCenter()
        self.backup()

        # wait till accept the center point.
        self.center_window.loop.exec_()

    def imageWindowLauncher(self):

        # Need a catch in here to only call once

        self.setEnabled(False)
        self.image_window = ImageWindow(self.image_dir, self.backup_dir, self)
        self.image_window.show()
        app.processEvents()
        self.image_window.loadImage()
        self.backup()

    def _getImageName(self):

        if self.image_count == None:
            image_list = sorted(glob.glob(self.image_dir + '/*.nef'))
            if not image_list:
                self.image_count = 1
            else:
                self.image_count = int(image_list[-1].split('_')[-2])
                self.image_count += 1
        # else:
        #     #Determine what number we are on
        #     # image_list = sorted(glob.glob(self.image_dir + '/*.NEF'))
        #     self.image_count = int(image_list[-1].split('_')[-2])
        #     self.image_count += 1

        # Now create a new name
        self.surface_path = f"IMG_{self.image_count:04}_surface"
        self.scatter_path = f"IMG_{self.image_count:04}_scatter"

        self.image_count += 1

    def center_point_txt(self):
        try:
            self.center_point_f = open(self.image_dir + '/csv_files/center_point.txt', 'r')
            self.position_list = self.center_point_f.readline().split(',')
            self.position = [np.float(self.position_list[0]), np.float(self.position_list[1])]
            self.cropped_size = [np.float(self.position_list[2]), np.float(self.position_list[3])]
            self.block_size_rescale = np.float(self.position_list[4])
        except FileNotFoundError:
            print("'center_point.txt' not exist, creating...")
            try:
                self.center_point_f = open(self.image_dir + '/csv_files/center_point.txt', 'a+')
            except FileExistsError:
                print("Fail to create {}, try to configure manually!".format(self.image_dir + '/csv_files/center_point.txt'))
                quit()

        if(int(self.scatter_path.split("_")[1]) != 1 and self.position==""):
            print("Missing center_point!")
            quit()

    def init_cnn_model(self):
        model_path = '/home2/tester/Desktop/recordingGui-master/best_model.pth'
        patch_size = 1
        n_input = 3 * ((2 * patch_size + 1) ** 2)
        n_out = 3
        n_hidden_layers = 1
        npl = 32
        model = FNN(n_input, n_out, n_hidden_layers, npl)
        model.load(model_path)
        model = model.cuda()
        return model

    def changeSurfaceSS(self):
        self.ss_drop_surface.setDisabled(False)

    def changedSurfaceSS(self):
        self.ss_drop_surface.setDisabled(True)

    def changeSurfaceISO(self):
        self.iso_drop_surface.setDisabled(False)

    def changedSurfaceISO(self):
        self.iso_drop_surface.setDisabled(True)

    def changeSurfaceFStop(self):
        self.fstop_drop_surface.setDisabled(False)

    def changedSurfaceFStop(self):
        self.fstop_drop_surface.setDisabled(True)

    def changeScatterSS(self):
        self.ss_drop_scatter.setDisabled(False)

    def changedScatterSS(self):
        self.ss_drop_scatter.setDisabled(True)

    def changeScatterISO(self):
        self.iso_drop_scatter.setDisabled(False)

    def changedScatterISO(self):
        self.iso_drop_scatter.setDisabled(True)

    def changeScatterFStop(self):
        self.fstop_drop_scatter.setDisabled(False)

    def save_info(self):
        try:
            camera_info = open('/home2/tester/Desktop/recordingGui-master/camera_info.txt', 'w')
            camera_info.write(str(self.ss_drop_surface.currentText())+' ')
            camera_info.write(str(self.ss_drop_scatter.currentText())+' ')
            camera_info.write(str(self.fstop_drop_surface.currentText())+' ')
            camera_info.write(str(self.fstop_drop_scatter.currentText())+' ')
            camera_info.write(str(self.iso_drop_surface.currentText())+' ')
            camera_info.write(str(self.iso_drop_scatter.currentText()))
            # camera_info.write(str(self.block_size_drop.currentText()))

            camera_info.close()
        except FileExistsError:
            print("Fail to create 'camera_info.txt'!")

    def changedScatterFStop(self):
        self.fstop_drop_scatter.setDisabled(True)

    # def _updateCapture(self):
    #     self.capture_button.setText("Capturing ...")
    #     self.capture_button.setFont(self.large_text)

    def capture_buttonClick(self):

        self.trim_first = True
        self.sect_first = True

        # Turn off both lights
        arduino.write(b'c')
        # time.sleep(0.1)

        # Get the image names
        self._getImageName()
        # self._updateCapture()

        self.center_point_txt()

        surface_iso = self.iso_drop_surface.currentText()
        surface_ss = self.ss_drop_surface.currentText()
        surface_fstop = self.fstop_drop_surface.currentText()

        # scatter_iso = self.iso_drop_scatter.currentIndex()
        # scatter_ss = self.ss_drop_scatter.currentIndex()
        # scatter_fstop = self.fstop_drop_scatter.currentIndex()

        scatter_iso = self.iso_drop_scatter.currentText()
        scatter_ss = self.ss_drop_scatter.currentText()
        scatter_fstop = self.fstop_drop_scatter.currentText()

        # Take the surface images
        ## Command to turn on light a
        # time_A = time.time()

        arduino.write(b'a')
        time.sleep(0.1)
        print('image 1: ')

        context = gp.Context()
        camera = gp.Camera()
        camera.init(context)

        # config_tree = camera.get_config(context)
        # print('=======')

        # quality = config_tree.get_child_by_name('imagequality')
        # for k in range(quality.count_choices()):
        #     choice = quality.get_choice(k)
        #     text_choice = '         - ' + choice
        #     print(text_choice)

        # total_child = config_tree.count_children()
        # for i in range(total_child):
        #     child = config_tree.get_child(i)
        #     text_child = '# ' + child.get_label() + ' ' + child.get_name()
        #     print(text_child)

        #     for a in range(child.count_children()):
        #         grandchild = child.get_child(a)
        #         text_grandchild = '    * ' + grandchild.get_label() + ' -- ' + grandchild.get_name()
        #         print(text_grandchild)

        #         try:
        #             text_grandchild_value = '        Setted: ' + grandchild.get_value()
        #             print(text_grandchild_value)
        #             # print('        Possibilities:')
        #             # for k in range(grandchild.count_choices()):
        #             #     choice = grandchild.get_choice(k)
        #             #     text_choice = '         - ' + choice
        #             #     print(text_choice)
        #         except:
        #             pass
        #         print()
        #     print()

        print('Capturing image')
        # # print(gp.GP_CAPTURE_IMAGE)
        config = camera.get_config(context)

        print(surface_fstop, surface_ss, surface_iso)

        capture_target_f = config.get_child_by_name('f-number')
        capture_target_f.set_value(surface_fstop)

        print(config.get_child_by_name('f-number').get_value())
        # # quit()
        # gp.check_result(gp.gp_widget_get_child_by_name(config, 'f-number'))        #    shutterspeed        iso
        # gp.gp_widget_set_value(capture_target_f, str(surface_fstop) )
        # # # print(capture_target_f.set_value('f/8'))

        capture_target_s = config.get_child_by_name('shutterspeed')
        capture_target_s.set_value(surface_ss)
        print(config.get_child_by_name('shutterspeed').get_value())

        capture_target_i = config.get_child_by_name('iso')
        capture_target_i.set_value(surface_iso)

        # # file_path = camera.capture(gp.GP_CAPTURE_IMAGE)

        camera.set_config(config)
        file_path = camera.trigger_capture()
        cnt = 0
        while cnt<2:
            event_type, event_data = camera.wait_for_event(10)
            if (event_type == gp.GP_EVENT_FILE_ADDED):
                cnt += 1
                print('Camera file path: {0}/{1}'.format(event_data.folder, event_data.name))
                target = os.path.join(self.image_dir, self.surface_path + '.' + event_data.name.split('.')[-1])

                print('Copying image to', target)
                cam_file = camera.file_get(event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)

                print(cam_file)
                cam_file.save(target)

        # # sp.call(['xdg-open', target])
        # # camera.exit()
        # print("finished")
        # quit()

        # camera = gp.check_result(gp.gp_camera_new())
        # gp.check_result(gp.gp_camera_init(camera))
        # get configuration tree

        # print(gp.gp_widget_count_children(config))

        # p = sp.Popen(["gphoto2",
        #               "--set-config-index", f"iso={surface_iso}",
        #               "--set-config-index", f"shutterspeed={surface_ss}",
        #               "--set-config-index", f"f-number={surface_fstop}",
        #               f"--filename={self.surface_path + '.%C'}",
        #               "--set-config", "viewfinder=1",
        #               # "--wait-event=0.5s",
        #               "--capture-image-and-download"],
        #               stdout=sp.PIPE, cwd=f"{self.image_dir}/")
        # print("image1 mid")
        # sout, _ = p.communicate()
        # p.wait()
        print("image1 finished")

        arduino.write(b'b')
        time.sleep(0.1)
        # print('image 2: ' + scatter_iso)

        print('Capturing scatter image')

        print(scatter_fstop, scatter_ss, scatter_iso)

        capture_target_f.set_value(scatter_fstop)
        capture_target_s.set_value(scatter_ss)
        capture_target_i.set_value(scatter_iso)

        camera.set_config(config)
        file_path = camera.trigger_capture()
        cnt = 0
        while cnt<2:
            event_type, event_data = camera.wait_for_event(10)
            if (event_type == gp.GP_EVENT_FILE_ADDED):
                cnt += 1
                print('Camera file path: {0}/{1}'.format(event_data.folder, event_data.name))
                target = os.path.join(self.image_dir, self.scatter_path + '.' + event_data.name.split('.')[-1])

                print('Copying image to', target)
                cam_file = camera.file_get(event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)

                print(cam_file)
                cam_file.save(target)
        camera.exit(context)

        # p = sp.Popen(["gphoto2",
        #               "--set-config-index", f"iso={scatter_iso}",
        #               "--set-config-index", f"shutterspeed={scatter_ss}",
        #               "--set-config-index", f"f-number={scatter_fstop}",
        #               f"--filename={self.scatter_path + '.%C'}",
        #               "--set-config", "viewfinder=1",
        #               # "--wait-event=0.5s",
        #               "--capture-image-and-download"],
        #               stdout=sp.PIPE, cwd=f"{self.image_dir}/")
        # sout, _ = p.communicate()
        # p.wait()

        print("finished")

        # Turn off both lights
        arduino.write(b'c')
        # self.capture_button.setText("Capture Image")
        # self.capture_button.setFont(self.large_text)

        # time_B = time.time()
        # print("time cost: ", time_B-time_A)
        #  # time cost:  8.700865745544434
        #  # time cost:  15.92504096031189

        # quit()

        ######## If 1st capture, collect the center point #######
        # print(self.surface_path)
        if(int(self.scatter_path.split("_")[1]) == 1):
            self.centerCapture()

        self.center_point_f.close()

        self.imageWindowLauncher()

        self.backup()

    def main_button_clicked(self):
        self.section_a = False
        self.section_b = False
        self.section_c = False
        self.section_count += 1

    def _initTableLayout(self):
        '''Initalize the table layout'''
        # Initalize the table assuming no CSV file
        self.main_table = QTableWidget(10,7)
        self.columnLabels = ["File Name","Distance","Sections",
                             "Shutter Speed", "ISO", "Aperature", "Notes"]
        self.main_table.setHorizontalHeaderLabels(self.columnLabels)
        # self.save_and_exit = QPushButton("save & exit")

        # Set buttons for the camera settings for the surface image
        surface_label = QLabel("Surface Image Settings")
        self.iso_button_surface = QPushButton("ISO")
        self.fstop_button_surface = QPushButton("FStop")
        self.ss_button_surface = QPushButton("SS")

        # Set buttons for the camera settings for the scatter image
        scatter_label = QLabel("Scatter Image Settings")
        self.iso_button_scatter = QPushButton("ISO")
        self.fstop_button_scatter = QPushButton("FStop")
        self.ss_button_scatter = QPushButton("SS")

        self.save_info_button = QPushButton("Save")

        # self.save_and_exit.clicked.connect(self.save_amendment_and_exit)

        # Connect the buttons for the surface image
        self.iso_button_surface.clicked.connect(self.changeSurfaceISO)
        self.fstop_button_surface.clicked.connect(self.changeSurfaceFStop)
        self.ss_button_surface.clicked.connect(self.changeSurfaceSS)

        # Connect the buttons for the scatter image
        self.iso_button_scatter.clicked.connect(self.changeScatterISO)
        self.fstop_button_scatter.clicked.connect(self.changeScatterFStop)
        self.ss_button_scatter.clicked.connect(self.changeScatterSS)
        self.save_info_button.clicked.connect(self.save_info)

        # Set the line edits for the settings
        self.ss_values = ['0.0001s', '0.0002s', '0.0003s', '0.0004s', '0.0005s', '0.0006s', '0.0008s', '0.0010s',
                          '0.0012s', '0.0015s', '0.0020s', '0.0025s', '0.0031s', '0.0040s', '0.0050s', '0.0062s',
                          '0.0080s', '0.0100s', '0.0125s', '0.0166s', '0.0200s', '0.0250s', '0.0333s', '0.0400s',
                          '0.0500s', '0.0666s', '0.0769s', '0.1000s', '0.1250s', '0.1666s', '0.2000s', '0.2500s',
                          '0.3333s', '0.4000s', '0.5000s', '0.6250s', '0.7692s', '1.0000s', '1.3000s', '1.6000s',
                          '2.0000s', '2.5000s', '3.0000s', '4.0000s', '5.0000s', '6.0000s', '8.0000s', '10.0000s',
                          '13.0000s', '15.0000s', '20.0000s', '25.0000s', '30.0000s']
        self.ss_drop_surface = QComboBox()
        self.ss_drop_surface.addItems(self.ss_values)
        # self.ss_drop_surface.setCurrentIndex(self.ss_values.index('0.0250s'))
        # self.ss_drop_surface.currentIndexChanged.connect(self.changedSurfaceSS)

        self.ss_drop_scatter = QComboBox()
        self.ss_drop_scatter.addItems(self.ss_values)
        # self.ss_drop_scatter.setCurrentIndex(self.ss_values.index('0.0031s'))
        # self.ss_drop_scatter.currentIndexChanged.connect(self.changedScatterSS)

        self.fstop_values = ['f/3', 'f/3.2', 'f/3.5', 'f/4', 'f/4.5', 'f/5', 'f/5.6', 'f/6.3', 'f/7.1', 'f/8', 'f/9',
                             'f/10', 'f/11', 'f/13', 'f/14', 'f/16', 'f/18', 'f/20', 'f/22', 'f/25', 'f/29', 'f/32']

        self.fstop_drop_surface = QComboBox()
        self.fstop_drop_surface.addItems(self.fstop_values)
        # self.fstop_drop_surface.setCurrentIndex(self.fstop_values.index('f/8'))
        # self.fstop_drop_surface.currentIndexChanged.connect(self.changedSurfaceFStop)

        self.fstop_drop_scatter = QComboBox()
        self.fstop_drop_scatter.addItems(self.fstop_values)
        # self.fstop_drop_scatter.setCurrentIndex(self.fstop_values.index('f/8'))
        # self.fstop_drop_scatter.currentIndexChanged.connect(self.changedScatterFStop)

        self.iso_values = ['100', '125', '160', '200', '250', '320', '400', '500', '640', '800', '1000', '1250',
        				   '1600', '2000', '2500', '3200', '4000', '5000', '6400', '8000', '10000', '12800', '25600']

        self.iso_drop_surface = QComboBox()
        self.iso_drop_surface.addItems(self.iso_values)
        # self.iso_drop_surface.setCurrentIndex(self.iso_values.index('100'))
        # self.iso_drop_surface.currentIndexChanged.connect(self.changedSurfaceISO)

        self.iso_drop_scatter = QComboBox()
        self.iso_drop_scatter.addItems(self.iso_values)
        # self.iso_drop_scatter.setCurrentIndex(self.iso_values.index('100'))
        # self.iso_drop_scatter.currentIndexChanged.connect(self.changedScatterISO)

        # Getting previous saved camera info
        # ss_value_surface ss_value_scatter fstop_value_surface fstop_value_scatter iso_value_surface iso_value_scatter
        try:
            camera_info = open('/home2/tester/Desktop/recordingGui-master/camera_info.txt', 'r')
            camera_info_list = camera_info.readline().split(' ')

            self.ss_drop_surface.setCurrentIndex(self.ss_values.index(camera_info_list[0]))
            self.ss_drop_surface.currentIndexChanged.connect(self.changedSurfaceSS)

            self.ss_drop_scatter.setCurrentIndex(self.ss_values.index(camera_info_list[1]))
            self.ss_drop_scatter.currentIndexChanged.connect(self.changedScatterSS)

            self.fstop_drop_surface.setCurrentIndex(self.fstop_values.index(camera_info_list[2]))
            self.fstop_drop_surface.currentIndexChanged.connect(self.changedSurfaceFStop)

            self.fstop_drop_scatter.setCurrentIndex(self.fstop_values.index(camera_info_list[3]))
            self.fstop_drop_scatter.currentIndexChanged.connect(self.changedScatterFStop)

            self.iso_drop_surface.setCurrentIndex(self.iso_values.index(camera_info_list[4]))
            self.iso_drop_surface.currentIndexChanged.connect(self.changedSurfaceISO)

            self.iso_drop_scatter.setCurrentIndex(self.iso_values.index(camera_info_list[5]))
            self.iso_drop_scatter.currentIndexChanged.connect(self.changedScatterISO)

            camera_info.close()

        except FileNotFoundError:
            print("'camera_info.txt' not exist, using default value...")
            self.ss_drop_surface.setCurrentIndex(self.ss_values.index('0.0250s'))
            self.ss_drop_surface.currentIndexChanged.connect(self.changedSurfaceSS)

            self.ss_drop_scatter.setCurrentIndex(self.ss_values.index('0.0031s'))
            self.ss_drop_scatter.currentIndexChanged.connect(self.changedScatterSS)

            self.fstop_drop_surface.setCurrentIndex(self.fstop_values.index('f/8'))
            self.fstop_drop_surface.currentIndexChanged.connect(self.changedSurfaceFStop)

            self.fstop_drop_scatter.setCurrentIndex(self.fstop_values.index('f/8'))
            self.fstop_drop_scatter.currentIndexChanged.connect(self.changedScatterFStop)

            self.iso_drop_surface.setCurrentIndex(self.iso_values.index('100'))
            self.iso_drop_surface.currentIndexChanged.connect(self.changedSurfaceISO)

            self.iso_drop_scatter.setCurrentIndex(self.iso_values.index('100'))
            self.iso_drop_scatter.currentIndexChanged.connect(self.changedScatterISO)

        # Set the font for the buttons
        self.iso_button_surface.setFont(self.small_text)
        self.fstop_button_surface.setFont(self.small_text)
        self.ss_button_surface.setFont(self.small_text)

        self.iso_button_scatter.setFont(self.small_text)
        self.fstop_button_scatter.setFont(self.small_text)
        self.ss_button_scatter.setFont(self.small_text)

        # Initially disable the table until the directories have been chosen
        self.main_table.setDisabled(True)
        self.iso_button_surface.setDisabled(True)
        self.fstop_button_surface.setDisabled(True)
        self.ss_button_surface.setDisabled(True)

        self.iso_button_scatter.setDisabled(True)
        self.fstop_button_scatter.setDisabled(True)
        self.ss_button_scatter.setDisabled(True)

        # make the table expand over the button
        self.main_table.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)

        # Center the lables
        scatter_label.setAlignment(QtCore.Qt.AlignCenter)
        surface_label.setAlignment(QtCore.Qt.AlignCenter)

        self.iso_drop_surface.setDisabled(True)
        self.fstop_drop_surface.setDisabled(True)
        self.ss_drop_surface.setDisabled(True)

        self.iso_drop_scatter.setDisabled(True)
        self.fstop_drop_scatter.setDisabled(True)
        self.ss_drop_scatter.setDisabled(True)

        header = self.main_table.horizontalHeader()

        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setSectionResizeMode(6, QHeaderView.Stretch)
        # header.setSectionResizeMode(2, QHeaderView.ResizeToContents)

        settings_layout = QGridLayout()
        settings_layout.addWidget(surface_label, 0, 0, 1, 2)
        settings_layout.addWidget(self.iso_button_surface, 1, 0)
        settings_layout.addWidget(self.fstop_button_surface, 2, 0)
        settings_layout.addWidget(self.ss_button_surface, 3, 0)
        settings_layout.addWidget(self.iso_drop_surface, 1, 1)
        settings_layout.addWidget(self.fstop_drop_surface, 2, 1)
        settings_layout.addWidget(self.ss_drop_surface, 3, 1)
        settings_layout.addWidget(scatter_label, 4, 0, 1, 2)
        settings_layout.addWidget(self.iso_button_scatter, 5, 0)
        settings_layout.addWidget(self.fstop_button_scatter, 6, 0)
        settings_layout.addWidget(self.ss_button_scatter, 7, 0)
        settings_layout.addWidget(self.iso_drop_scatter, 5, 1)
        settings_layout.addWidget(self.fstop_drop_scatter, 6, 1)
        settings_layout.addWidget(self.ss_drop_scatter, 7, 1)
        settings_layout.addWidget(self.save_info_button, 8, 0, 2, 1)
        settings_layout.setColumnStretch(0, 10)
        settings_layout.setColumnStretch(1, 30)
        # settings_layout.setColumnStretch(2, 10)

        # settings_layout.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred)
        # Make the talbe have its own layout
        # Not sure this is necessary as the table isn't that complicated
        table_layout = QGridLayout()
        table_layout.addWidget(self.main_table, 0, 0)
        # table_layout.addWidget(self.save_and_exit, 1, 0)

        layout = QGridLayout()
        layout.addLayout(table_layout, 0, 0)
        layout.addLayout(settings_layout, 0, 1)
        layout.setColumnStretch(0, 50)
        layout.setColumnStretch(1, 10)

        return layout

    def _initDirsLayout(self):

        # Add the directory buttons
        self.image_dir_button =  QPushButton("Select Image Folder")
        self.backup_dir_button =  QPushButton("Select Backup Folder")
        self.capture_button = QPushButton("Capture Images")

        # Add the directory labels
        self.image_dir_label = QLabel()
        self.backup_dir_label = QLabel()
        self.image_dist_label = QLabel()
        self.main_label = QLabel()
        self.main_button = QPushButton("Reset Sections")

        # Change the font of all the lables and buttons
        self.image_dir_label.setFont(self.small_text)
        self.backup_dir_label.setFont(self.small_text)
        self.image_dist_label.setFont(self.small_text)
        self.main_label.setFont(self.small_text)
        self.main_button.setFont(self.small_text)
        self.image_dir_button.setFont(self.small_text)
        self.backup_dir_button.setFont(self.small_text)
        self.capture_button.setFont(self.large_text)

        # Set the size policy for the label so it expands vertically
        self.main_label.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)
        self.main_button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.capture_button.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)

        # Initalize the messages for the main label and the directories
        msg = "Please select the Image and Backup Directories"
        self.image_dir = "/Path/to/image/directoy/"
        self.backup_dir = "/Path/to/backup/directoy/"
        self.main_message = msg

        # Set the labels with the inital messages
        self.image_dir_label.setText(self.image_dir)
        self.backup_dir_label.setText(self.backup_dir)
        self.main_label.setText(self.main_message)

        # Set the alignment of the main text
        self.main_label.setAlignment(QtCore.Qt.AlignCenter)
        self.main_label.setFont(self.large_text)

        # Connect the buttons to their functions
        self.image_dir_button.clicked.connect(self.image_dir_buttonClick)
        self.backup_dir_button.clicked.connect(self.backup_dir_buttonClick)
        self.capture_button.clicked.connect(self.capture_buttonClick)
        self.main_button.clicked.connect(self.main_button_clicked)

        # Diable the backup directory button at first
        self.backup_dir_button.setEnabled(False)
        self.capture_button.setEnabled(False)
        self.main_button.setDisabled(True)

        # Define the layout for the main window
        layout = QGridLayout()
        layout.setColumnStretch(0, 0)
        layout.setColumnStretch(1, 1)
        layout.setColumnStretch(2, 1)
        layout.addWidget(self.image_dir_button, 0, 0)
        layout.addWidget(self.image_dir_label, 0, 1)
        layout.addWidget(self.backup_dir_button, 1, 0)
        layout.addWidget(self.backup_dir_label, 1, 1)
        layout.addWidget(self.capture_button, 0, 2, 2, 1)
        layout.addWidget(self.main_label, 2, 1, 1, 2)
        layout.addWidget(self.main_button, 2, 0, 1, 1)

        return layout

    def _initHitLayout(self):

        # Make the hit button and the labels for the section distances
        self.trim_button = QPushButton("TRIM (10 μm)")
        self.sect_button = QPushButton("SECTION (5 μm)")

        self.image_dist_label = QLabel()
        self.total_dist_label = QLabel()

        # Set the font for the labels and button
        self.image_dist_label.setFont(self.large_text)
        self.total_dist_label.setFont(self.large_text)
        self.trim_button.setFont(self.large_text)
        self.sect_button.setFont(self.large_text)

        # Set the size policies for the buttons and labels
        self.trim_button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.sect_button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.image_dist_label.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)
        self.total_dist_label.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)

        # Initalize the distances
        self.image_distance = 0
        self.total_distance = 0

        # Make the labels for the distances
        # Keep the number separate from the text so they can easily be used elsewhere
        self.image_dist_label.setText('Image Distance : ' + str(self.image_distance) + ' μm')
        self.total_dist_label.setText('Total Distance : ' + str(self.total_distance) + ' μm')

        # Connect the hit button
        self.trim_button.clicked.connect(self.trim_buttonClick)
        self.sect_button.clicked.connect(self.sect_buttonClick)

        # Disable the hit button initially
        self.trim_button.setDisabled(True)
        self.sect_button.setDisabled(True)

        # Create the layout
        layout = QGridLayout()
        layout.addWidget(self.trim_button, 0, 0, 1, 2)
        layout.addWidget(self.sect_button, 1, 0, 1, 2)

        # layout.addWidget(self.trim_button, 0, 1, 1, 1)
        # layout.addWidget(self.sect_button, 1, 1, 1, 1)

        layout.addWidget(self.image_dist_label, 0, 2, 1, 2)
        layout.addWidget(self.total_dist_label, 1, 2, 1, 2)
        layout.setColumnStretch(0, 20)
        layout.setColumnStretch(1, 15)

        return layout

    def errorMessage(self, error):
       msg = QMessageBox()
       msg.setIcon(QMessageBox.Critical)
       msg.setText(error)
       # msg.setInformativeText("This is additional information")
       msg.setWindowTitle("ERROR")
       # msg.setDetailedText("The details are as follows:")
       msg.setStandardButtons(QMessageBox.Ok)
       # msg.buttonClicked.connect(msgbtn)

       msg.exec_()
       # print "value of pressed message box button:", retval

if __name__ == '__main__':

    app = QApplication(sys.argv)
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyside2())

    global arduino
    try:
        arduino = serial.Serial('/dev/ttyACM0', 9600)     #dev/cu.usbmodem14201
    except IOError:
        try:
            arduino = serial.Serial('/dev/cu.usbmodem14301', 9600)
        except IOError:
            try:
                arduino = serial.Serial('/dev/cu.usbmodem14401', 9600)
            except IOError:
                print("You don't have the right arduino port.")
                sys.exit()

    window = MainWindow()

    window.show()

    # Focus here
    p = sp.Popen(["/home2/tester/Desktop/recordingGui-master/focus.sh"], shell=True, stdout=sp.PIPE)

    # time.sleep(1)
    # arduino.write(b'a')

    # focus_window = FocusWindow(window)
    # focus_window.show()

    app.exec_()

    # window.watcher.stop()
    # window.thread.quit()
    # window.thread.wait()


from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('all-loans/', include('loans.urls')),
    path('payments/',include('mpesa_payments.urls')),
    path('api/', include(('core.routers', 'core'), namespace='core-api')),
]


import configparser
from requests import get
from urllib.parse import urljoin

class HASpotify:
    '''
    this uses home assistant to get the currently playing song album
    '''
    def __init__(self, **kwargs):
        config = configparser.ConfigParser()
        config.read("./config.ini")
        homeassistant = config['homeassistant']

        self.token = kwargs.get("token", homeassistant['token'])
        self.interface = kwargs.get("interace", homeassistant['interface'])
        entity_id = kwargs.get("entity_id", homeassistant['entity_id'])

        url = urljoin(self.interface, 'api/states/')
        self.url = urljoin(url, entity_id)

        self.headers = {
            "Authorization": f"Bearer {self.token}",
            "content-type": "application/json",
        }

    def album_cover_url(self):
        response = get(self.url, headers=self.headers)
        try:
            self.cover_url = urljoin(self.interface, response.json()['attributes']['entity_picture'])
        except KeyError:
            self.cover_url = None
        return self.cover_url

if __name__=="__main__":
    sp = HASpotify()


from nltk import Tree

word_tags = {
    'NP': ['NN', 'NP', 'NNP', 'NNS', 'NNPS', 'NX', 'WHNP'],
    'PP': ['PRP', 'PRP$', 'WP', 'WP$', 'PRP', 'PRP$'],
    'VP': ['VP', 'VB', 'VBD', 'VBG', 'VBN', 'VBP', 'VBZ', 'MD'],
    'ADJP': ['ADJP', 'JJ', 'JJR', 'JJS'],
    'ADVP': ['ADVP', 'WHADVP', 'RB', 'RBR', 'RBS', 'WRB'],
    'DET': ['DT', 'EX', 'PDT', 'WDT'],
    'ADP': ['IN'],
    'CONJP': ['CC'],
    'NUM': ['CD'],
    'PUNC': ['!', '#', '$', '``', "''", '(', ')', ',', '-LRB-', '-RRB-', '.', ':', '?', 'LST', 'PRN'],
    'PRT': ['PRT', 'PP', 'TO', 'POS', 'RP']}

pharse_tags = {
    'NP': ['NP', '@NP', 'NX', '@NX', 'WHNP', '@WHNP'],
    'VP': ['VP', '@VP'],
    'ADJP': ['ADJP', '@ADJP', 'WHADJP', '@WHADJP'],
    'ADVP': ['ADVP', '@ADVP', 'WHADVP', '@WHADVP'],
    'S': ['S', '@S', 'SBAR', '@SBAR', 'SQ', '@SQ', 'SINV', '@SINV'],
    'ROOT': ['ROOT'],
    'CONJP': ['CONJP', '@CONJP'],
    'NUM': ['QP', '@QP'],
    'PUNC': ['LST', '@LST'],
    'PRT': ['PRT', '@PRT', 'PP', '@PP', 'WHPP', '@WHPP']}

root_merged_phrase_tags = {
    'NP': ['NP', '@NP', 'NX', '@NX', 'WHNP', '@WHNP'],
    'VP': ['VP', '@VP'],
    'ADJP': ['ADJP', '@ADJP', 'WHADJP', '@WHADJP'],
    'ADVP': ['ADVP', '@ADVP', 'WHADVP', '@WHADVP'],
    'S': ['S', '@S', 'SBAR', '@SBAR', 'SQ', '@SQ', 'SINV', '@SINV', 'ROOT'],
    'CONJP': ['CONJP', '@CONJP'],
    'NUM': ['QP', '@QP'],
    'PUNC': ['LST', '@LST'],
    'PRT': ['PRT', '@PRT', 'PP', '@PP', 'WHPP', '@WHPP']}

def parse(s):
    result = []
    for t in s.split():
        if t.startswith("("):
            result.append("(")
            result.append(t[1:])
        elif t.endswith(")"):
            n = sum([1 for x in t if x == ")"])
            result.append(t[:-n])
            result.extend([")"] * n)
        else:
            result.append(t)

    return result

def preprocess_WORD(s):
    return Tree.fromstring(s).leaves()

def preprocess_WORD_TAG(s):
    pos = [t for w, t in Tree.fromstring(s).pos()]

    new_pos = []
    for t in pos:
        added = False
        for k in word_tags.keys():
            if t in word_tags[k]:
                new_pos.append(k)
                added = True
        if not added:
            new_pos.append('X')
    return new_pos

def preprocess_PARSE(s):
    s = parse(s)

    new_s = []
    for i, t in enumerate(s):
        if (t == '(' and s[i + 3] != ')') or (t == ')' and s[i - 3] != '('):
            new_s.append(t)
        elif i >= 2 and i < len(s) - 2 and s[i - 2] == '(' and s[i + 1] == ')':
            new_s.append(t)

    s = ['reduce' if t == ')' else 'shift' for t in new_s if t != '(']
    return s

def preprocess_CONS_TAG_root_divided(s):
    s = parse(s)
    new_s = []
    for i, t in enumerate(s):
        if t == ")":
            cnt = 0
            cnt_max = 0
            for j in range(i, -1, -1):
                if s[j] == "(":
                    cnt -= 1
                elif s[j] == ")":
                    cnt += 1
                    if cnt > cnt_max:
                        cnt_max = cnt

                if cnt == 0 and cnt_max > 1:
                    new_s.append(s[j + 1])
                    break

    new_new_s = []
    for t in new_s:
        added = False
        for k in pharse_tags.keys():
            if t in pharse_tags[k]:
                new_new_s.append(k)
                added = True
        if not added:
            new_new_s.append('X')

    return new_new_s

def preprocess_CONS_TAG_root_merged(s):
    s = parse(s)
    new_s = []
    for i, t in enumerate(s):
        if t == ")":
            cnt = 0
            cnt_max = 0
            for j in range(i, -1, -1):
                if s[j] == "(":
                    cnt -= 1
                elif s[j] == ")":
                    cnt += 1
                    if cnt > cnt_max:
                        cnt_max = cnt

                if cnt == 0 and cnt_max > 1:
                    new_s.append(s[j + 1])
                    break

    new_new_s = []
    for t in new_s:
        added = False
        for k in root_merged_phrase_tags.keys():
            if t in root_merged_phrase_tags[k]:
                new_new_s.append(k)
                added = True
        if not added:
            new_new_s.append('X')

    return new_new_s