"""Some Signals & Systems Helping Routines."""
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from matplotlib.patches import Circle, Rectangle
import numpy as np
from scipy import signal
from scipy.special import diric

def plot_zplane(z, p, k):
    """Plot pole/zero/gain plot of discrete-time, linear-time-invariant system.

    Note that the for-loop handling might be not very efficient
    for very long FIRs

    z...array of zeros in z-plane
    p...array of poles in z-zplane
    k...gain factor

    """
    # draw unit circle
    Nf = 2**7
    Om = np.arange(Nf) * 2*np.pi/Nf
    plt.plot(np.cos(Om), np.sin(Om), 'C7')

    rect_box = Rectangle((-3, -3), 6, 6, color='yellowgreen', alpha=0.33)
    plt.gcf().gca().add_artist(rect_box)
    try:  # TBD: check if this pole is compensated by a zero
        circle = Circle((0, 0), radius=np.max(np.abs(p)),
                        color='white', alpha=1)
        plt.gcf().gca().add_artist(circle)
    except ValueError:
        print('no pole at all, ROC is whole z-plane')

    zu, zc = np.unique(z, return_counts=True)  # find and count unique zeros
    for zui, zci in zip(zu, zc):  # plot them individually
        plt.plot(np.real(zui), np.imag(zui), ms=7,
                 color='C0', marker='o', fillstyle='none')
        if zci > 1:  # if multiple zeros exist then indicate the count
            plt.text(np.real(zui), np.imag(zui), zci)

    pu, pc = np.unique(p, return_counts=True)  # find and count unique poles
    for pui, pci in zip(pu, pc):  # plot them individually
        plt.plot(np.real(pui), np.imag(pui), ms=7,
                 color='C3', marker='x')
        if pci > 1:  # if multiple poles exist then indicate the count
            plt.text(np.real(pui), np.imag(pui), pci)

    plt.text(0, +1, 'k=%f' % k)
    plt.text(0, -1, 'ROC for causal: green')
    plt.axis('square')
    # plt.axis([-2, 2, -2, 2])
    plt.xlabel(r'$\Re\{z\}$')
    plt.ylabel(r'$\Im\{z\}$')
    plt.grid(True)

def interpolate_dft2dtft(X, W):
    """DFT to DTFT interpolation.

    This is the reconstruction filter in frequency domain to get
    a finite length time sequence starting from k=0 out of a N-periodic
    sequence

    X...array containing DFT spectrum
    W...array with normalized digital frequencies
    typically W = np.arange(Nint) * 2*np.pi/Nint with desired Nint

    see e.g.
    Rabiner, Gold, 1975, Theory and Application of Digital Signal Processing
    Prentice Hall, eq. (2.142)

    """
    N = np.size(X)  # we estimate the DFT length from the DFT spectrum
    tmp_2piN = 2*np.pi/N
    tmp_N2 = (N-1)/2
    Xint = np.zeros_like(W, dtype='complex')
    for cW, vW in enumerate(W):  # counter, value
        for cX, vX in enumerate(X):
            W_tmp = vW - tmp_2piN * cX
            Xint[cW] += vX * diric(W_tmp, N) * np.exp(-1j*W_tmp*tmp_N2)
    return Xint

def plot_dtlti_analysis(z, p, k, fs=1, Nf=2**10, Nt=2**5):
    """Plot linear, time-invariant, discrete-time system.

    Impulse, step, frequency response (level/phase/group delay) and z-plane
    of transfer function H(z) given as zeros/poles/gain description

    Note that we use fs only for the frequency responses

    """
    # still hard coded, TBD:
    # figure size
    # group_delay discontinuities and automatic ylim, yticks is not
    # useful sometimes

    plt.figure(figsize=(9, 9), tight_layout=True)

    mu = np.arange(Nf)
    df = fs/Nf
    dW = 2*np.pi/Nf
    f = mu*df  # frequency vector [0...fs)
    W = mu*dW  # digital angular frequency [0...2pi)

    sys = signal.dlti(z, p, k, dt=True)
    sys_ba = signal.TransferFunction(sys)
    b = sys_ba.num   # we need coeff b,a for group_delay()
    a = sys_ba.den

    [W, H] = signal.dlti.freqresp(sys, W)
    [W, gd] = signal.group_delay((b, a), w=W)  # gd in samples
    h = signal.dimpulse(sys, n=Nt)
    he = signal.dstep(sys, n=Nt)

    # plot frequency response: level
    ax1 = plt.subplot(3, 2, 1)
    ax1t = ax1.twiny()

    ax1.grid(True, color='lavender', which='major')
    # plotted below grid :-(
    # ax1t.grid(True, color='mistyrose', which='minor')

    ax1.plot(W/(2*np.pi), 20*np.log10(np.abs(H)), color='C0', lw=2)
    ax1.set_xscale('linear')
    ax1.set_xlim([0, 1])
    ax1.tick_params(axis='x', labelcolor='C0')
    ax1.set_xlabel(r'$\frac{\Omega}{2\pi} = \frac{f}{f_s}$', color='C0')
    ax1.set_xticks(np.arange(11)/10)
    ax1.set_ylabel('level in dB   or   20 lg|H| / dB', color='k')
    ax1.set_axisbelow(True)

    ax1t.plot(f, 20*np.log10(np.abs(H)), color='C3', lw=2)
    ax1t.set_xscale('log')
    ax1t.set_xlim([f[1], f[-1]])
    ax1t.tick_params(axis='x', labelcolor='C3')
    ax1t.set_xlabel('frequency in Hz   or   f / Hz for $f_s$ = ' +
                    '{0:5.2f}'.format(fs)+' Hz', color='C3')
    ax1t.set_axisbelow(True)

    # plot impulse response
    ax2 = plt.subplot(3, 2, 2)
    ax2.stem(np.squeeze(h[0]), np.squeeze(h[1]),
             use_line_collection=True,
             linefmt='C0:', markerfmt='C0o', basefmt='C0:')
    ax2.xaxis.set_major_locator(MaxNLocator(integer=True))
    ax2.grid(True)
    ax2.set_xlabel(r'$k$')
    ax2.set_ylabel(r'impulse response $h[k]$')

    # plot frequency response: phase
    ax3 = plt.subplot(3, 2, 3)
    ax3.plot(W, np.unwrap(np.angle(H)))
    ax3.set_xlabel(
        r'digital angular frequency in radian   or   $\Omega$ / rad')
    ax3.set_ylabel(r'phase in radian   or $\angle$ H / rad')
    ax3.set_xlim([0, 2*np.pi])
    ax3.set_xticks(np.arange(9)*np.pi/4)
    ax3.set_xticklabels([r'0', r'$\pi/4$', r'$\pi/2$', r'$3/4\pi$',
                         r'$\pi$', r'$5/4\pi$', r'$3/2\pi$', r'$7/4\pi$',
                         r'$2\pi$'])
    # ax3.set_ylim([-180, +180])
    # ax3.set_yticks(np.arange(-180, +180+45, 45))
    ax3.grid(True)

    # plot step response
    ax4 = plt.subplot(3, 2, 4)
    ax4.stem(np.squeeze(he[0]), np.squeeze(he[1]), use_line_collection=True,
             linefmt='C0:', markerfmt='C0o', basefmt='C0:')
    ax4.xaxis.set_major_locator(MaxNLocator(integer=True))
    ax4.grid(True)
    ax4.set_xlabel(r'$k$')
    ax4.set_ylabel(r'step response $h_\epsilon[k]$')

    # plot frequency response: group delay
    ax5 = plt.subplot(3, 2, 5)
    ax5.plot(W/np.pi, gd/fs)
    ax5.set_xscale('linear')
    ax5.set_xlim([0, 2])
    ax5.set_xlabel(r'$\frac{\Omega}{\pi}$')
    ax5.set_ylabel(r'group delay in seconds   or   $\tau_\mathrm{GD}$ / s')
    ax5.grid(True, which='both')

    # zplane
    # ax6 =
    plt.subplot(3, 2, 6)
    plot_zplane(sys.zeros, sys.poles, sys.gain)  # see function above

    # plt.tight_layout(True)

def plot_splane(z, p, k):
    """Plot pole/zero/gain plot of continuous-time, linear-time-invariant system.

    Note that the for-loop handling might be not very efficient
    for very long FIRs

    z...numpy array of zeros in s-plane
    p...numpy array of poles in s-zplane
    k...gain factor

    """
    z = np.array(z)
    p = np.array(p)
    i = 0
    j = 0
    while i < z.size:
        while j < p.size:
            if z[i] == p[j]:
                z = np.delete(z, i)
                p = np.delete(p, j)
                i = i - 1
                break
            else:
                j = j + 1
        j = 0
        i = i + 1
    try:
        xmax = np.amax(np.real(z))
        xmin = np.amin(np.real(z))
        ymax = np.amax(np.imag(z))
        ymin = np.amin(np.imag(z))
    except ValueError:  # z is empty
        xmax = ymax = 2
        xmin = ymin = -2
    try:
        pp = np.amax(np.real(p))
        xmax = max(xmax, pp)
        xmin = min(xmin, np.amin(np.real(p)))
        ymax = max(ymax, np.amax(np.imag(p)))
        ymin = min(ymin, np.amin(np.imag(p)))
    except ValueError:  # p is empty
        pp = 1j

    zu, zc = np.unique(z, return_counts=True)  # find and count unique zeros
    for zui, zci in zip(zu, zc):  # plot them individually
        plt.plot(np.real(zui), np.imag(zui), ms=7,
                 color='C0', marker='o', fillstyle='none')
        if zci > 1:  # if multiple zeros exist then indicate the count
            plt.text(np.real(zui), np.imag(zui), zci)

    pu, pc = np.unique(p, return_counts=True)  # find and count unique poles
    for pui, pci in zip(pu, pc):  # plot them individually
        plt.plot(np.real(pui), np.imag(pui), ms=7,
                 color='C3', marker='x')
        if pci > 1:  # if multiple poles exist then indicate the count
            plt.text(np.real(pui), np.imag(pui), pci)
    eps = 2
    if np.abs(xmax - xmin) < 2:
        xmax = xmin + np.abs(xmax-xmin)/2+eps
        xmin = xmax - 2*eps
    else:
        a = np.abs(xmax-xmin)/2
        xmin = xmin - a
        xmax = xmax + a
    if np.abs(ymax-ymin) < 2:
        ymax = ymin + np.abs(ymax-ymin)/2 + eps
        ymin = ymax - 2*eps
    else:
        a = np.abs(ymax - ymin)/2
        ymax = ymax + a
        ymin = ymin - a
    plt.axis([xmin, xmax, ymin, ymax])
    if np.imag(pp) == 0:
        plt.plot([pp, pp], [2*ymin, 2*ymax], color='brown')
        plt.fill_between([pp, 2*xmax], [2*ymax, 2*ymax],
                         2*ymin, color='yellowgreen', alpha=0.33, label='ROC')
    else:
        plt.fill_between([-2*xmax, 2*xmax], [2*ymax, 2*ymax],
                         2*ymin, color='yellowgreen', alpha=0.33, label='ROC')
    plt.xlabel(r'$\Re\{s\}$')
    plt.ylabel(r'$\Im\{s\}$')
    plt.legend()
    plt.text(xmin + np.abs(xmin) / 5, ymin + np.abs(ymin) / 5, 'k=%f' % k)
    plt.grid(True)

def group_delay(z, p, w):
    """
    group delay of a system function

    Parameters
    -----------
        z:  array, zeros in s-plane
        p:  array, poles in s-plane
        w:  array, angular frequencies

    Returns
    -----------
        w:  array, angular frequencies
        gd: float, group delay
    """
    if z.size == 0 and p.size == 0:
        return w, np.zeros(w.size)
    gd = 0
    for i in z:
        gd += np.real(i)/((w-np.imag(i))**2+np.real(i)**2)
    for i in p:
        gd -= np.real(i)/((w-np.imag(i))**2+np.real(i)**2)
    return w, gd

def plot_clti_analysis(z, p, k,):
    """Plot linear, time-invariant, discrete-time system.

    Parameter
    ------------
        z:  array, zeros of system function
        p:  array, poles of system function
        k:  float, gain factor
    Return
    ------------
        impulse response plot
        step response plot
        frequency response (level/phase/group delay)
        s-plane of transfer function H(s) given as zeros/poles/gain desciption
    """
    z = np.array(z)
    p = np.array(p)
    # still hard coded, TBD:
    # figure size

    plt.figure(figsize=(9, 9), tight_layout=True)

    sys = signal.lti(z, p, k)
    # sys_ba = signal.TransferFunction(sys)
    # b = sys_ba.num   # we needed coeff b,a for group_delay()
    # a = sys_ba.den

    [W, H] = signal.lti.freqresp(sys)
    F = W/(2*np.pi)
    gd = group_delay(z=z, p=p, w=W)[1]  # gd in samples using poles / zeros
    h = signal.impulse(sys)
    he = signal.step(sys)

    # plot frequency response: level
    ax1 = plt.subplot(3, 2, 1)
    ax1t = ax1.twiny()

    ax1.grid(True, color='lavender', which='major')

    ax1.plot(W/(2*np.pi), 20*np.log10(np.abs(H)), color='C0', lw=2)
    ax1.set_xscale('linear')
    ax1.set_xlim([F[0], F[F.size-1]])
    ax1.tick_params(axis='x', labelcolor='C0')
    ax1.set_xlabel(r'$f$ $/$ Hz', color='C0')
    ax1.set_ylabel('level in dB   or   20 lg|H| / dB', color='k')
    ax1.set_axisbelow(True)

    ax1t.plot(W/(2*np.pi), 20*np.log10(np.abs(H)), color='C3', lw=2)
    ax1t.set_xscale('log')
    ax1t.set_xlim([F[0], F[F.size-1]])
    ax1t.tick_params(axis='x', labelcolor='C3')
    ax1t.set_xlabel('$f$ $/$ Hz', color='C3')
    ax1t.set_axisbelow(True)

    # plot impulse response
    ax2 = plt.subplot(3, 2, 2)
    ax2.plot(np.squeeze(h[0]), np.squeeze(h[1]))
    ax2.xaxis.set_major_locator(MaxNLocator(integer=True))
    ax2.grid(True)
    ax2.set_xlabel(r'$t$ $/$ $s$')
    ax2.set_ylabel(r'impulse response $h(t)$')

    # plot frequency response: phase
    ax3 = plt.subplot(3, 2, 3)
    ax3.plot(W/(2*np.pi), np.unwrap(np.angle(H)))
    ax3.set_xlabel(
        r'$f$ $/$ Hz')
    ax3.set_ylabel(r'phase in radian   or $\angle$ H / rad')
    ax3.set_xlim([F[0], F[F.size-1]])
    ax3.grid(True)

    # plot step response
    ax4 = plt.subplot(3, 2, 4)
    ax4.plot(np.squeeze(he[0]), np.squeeze(he[1]))
    ax4.xaxis.set_major_locator(MaxNLocator(integer=True))
    ax4.grid(True)
    ax4.set_xlabel(r'$t$ $/$ $s$')
    ax4.set_ylabel(r'step response $h_\epsilon(t)$')

    # plot frequency response: group delay
    ax5 = plt.subplot(3, 2, 5)
    ax5.plot(W, gd)
    ax5.set_xscale('log')
    ax5.set_xlim([W[0], W[W.size-1]])
    ax5.set_xlabel(r'$\omega$ $/$ $\frac{\mathrm{rad}}{s}$')
    ax5.set_ylabel(r'group delay in seconds   or   $\tau_\mathrm{GD}$ / s')
    ax5.grid(True, which='both')

    # splane
    # ax6 =
    plt.subplot(3, 2, 6)
    plot_splane(sys.zeros, sys.poles, sys.gain)  # see function above

    # plt.tight_layout(True)
    return W, H


from pctasks.core.cosmos.settings import CosmosDBSettings
from pctasks.run.settings import RunSettings, WorkflowRunnerType
from pctasks.run.workflow.argo import ArgoWorkflowRunner
from pctasks.run.workflow.base import WorkflowRunner
from pctasks.run.workflow.local import LocalWorkflowRunner

def get_workflow_runner() -> WorkflowRunner:
    run_settings = RunSettings.get()
    cosmosdb_settings = CosmosDBSettings.get()

    if run_settings.workflow_runner_type == WorkflowRunnerType.LOCAL:
        assert run_settings.local_dev_endpoints_url  # Checked during validation
        return LocalWorkflowRunner(run_settings, cosmosdb_settings)
    elif run_settings.workflow_runner_type == WorkflowRunnerType.ARGO:
        return ArgoWorkflowRunner(run_settings, cosmosdb_settings)
    else:
        raise ValueError(
            f"Unknown workflow runner type: {run_settings.workflow_runner_type}"
        )


import os.path as osp
import random

from sklearn.metrics import f1_score

from utils import helper, simulation, keypoint_function
from scipy.sparse import coo_matrix
import torch
from torch.nn import Dropout
import torch.nn.functional as F
from torch_geometric.data import Data, DataLoader
from torch_geometric.nn import GraphUNet
from torch_geometric.utils import dropout_adj
from torchvision import transforms
import numpy as np
from models.datasets import SimDataset
import cv2
from models.datasets import create_tumor_set, create_sift_tumor_set, random_pixel_tumor_set

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        pool_ratios = [.75, 0.5]
        self.unet = GraphUNet(3, 200, 2,
                              depth=3, pool_ratios=pool_ratios)

    def forward(self):
        edge_index, _ = dropout_adj(data.edge_index, p=0.2,
                                    force_undirected=True,
                                    num_nodes=data.num_nodes,
                                    training=self.training)
        x = F.dropout(data.x, p=0.92, training=self.training)
        x = self.unet(x, edge_index)
        return F.log_softmax(x, dim=1)

def train():
    model.train()
    optimizer.zero_grad()
    F.nll_loss(model()[data.train_mask], data.y[data.train_mask]).backward()
    optimizer.step()

def test():
    model.eval()
    logits, accs = model(), []
    for _, mask in data('train_mask', 'val_mask', 'test_mask'):
        pred = logits[mask].max(1)[1]
        acc = pred.eq(data.y[mask]).sum().item() / mask.sum().item()
        accs.append(acc)
    return accs

if __name__ == '__main__':
    # dataset = create_tumor_set()
    # dataset1, dataset_alt = create_sift_tumor_set()
    dataset2 = random_pixel_tumor_set()
    train_data = dataset2[:15]
    test_data = dataset2[15:]
    dataset_shuffled = random.shuffle(dataset2)
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model = Net().to(device)
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=0.001)

    best_val_acc = test_acc = 0

    for epoch in range(1, 301):
        for data in dataset2:
            train()
        train_acc, val_acc, tmp_test_acc = test()
        if val_acc > best_val_acc:
            best_val_acc = val_acc
            test_acc = tmp_test_acc
        log = 'Epoch: {:03d}, Train: {:.4f}, Val: {:.4f}, Test: {:.4f}'
        print(log.format(epoch, train_acc, best_val_acc, test_acc))


from flasgger import Swagger
from flask import Flask

from sdk.limiter.config.limiter import ServerLimiterConfig
from sdk.limiter.rate_limiter import RateLimiter
from sdk.phoenix.config.server_config import SwaggerConfig

def configure_rate_limit(app: Flask, config: ServerLimiterConfig):
    default_limiter = RateLimiter(
        app,
        default_limit=config.default,
        storage_uri=config.storageUri,
    )
    return default_limiter

def configure_swagger(app, config: SwaggerConfig, api_specs):
    template = {
        "swagger": "2.0",
        "headers": [
            ("Access-Control-Allow-Origin", "*"),
            ("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"),
            ("Access-Control-Allow-Credentials", "true"),
        ],
        "info": {
            "title": "Huma SDK API",
            "description": "Rest API for Huma SDK APIs",
        },
        "schemes": ["http", "https"],
        "tags": [
            {
                "name": "storage",
                "description": "The storage API a.k.a object storage like s3",
            },
            {"name": "auth", "description": "The authentication API"},
        ],
    }

    if config.template:
        config_template = config.template
        config_tags = config_template.get("tags", [])
        default_tags = template.get("tags")
        tags = [*default_tags, *config_tags]
        template = config_template
        template["tags"] = tags

    specs = [
        {
            "endpoint": "apispec_all",
            "route": "/apispec_all.json",
            "rule_filter": lambda rule: True,  # all in
            "model_filter": lambda tag: True,  # all in
        },
    ]
    specs = specs + api_specs
    swagger_config = {
        "headers": [],
        "specs": specs,
        "static_url_path": "/flasgger_static",
        # "static_folder": "static",  # must be set by user
        "swagger_ui": True,
        "specs_route": config.specs_route,
    }
    return Swagger(app, template=template, config=swagger_config)


import decimal
from decimal import * # 부동소수점 정밀도 계산하는 모듈
#부동 소수점 정밀도 -> 부동소수점 숫자는 이진수로 표시 / 특정 소수점을 2진수로 표시가 안되는 경우가 발생한다.
#(90.7+30.2)+0.0002, print(120.9) -> 부동 소숫점 반올림 오류 (120.90020000000001)

def case01():
    res = Decimal("90.7") + Decimal("30.2") + Decimal("0.0002")
    print(res,type(res))    #120.9002 <class 'decimal.Decimal'>
    print(1/3)              #0.3333333333333333

    print(decimal.getcontext()) #부동소수점 환경설정값을 출력, Context라는 클래스로 속성 리턴
    decimal.getcontext().prec=3
    a, b = Decimal("1"), Decimal("3")
    print(a/b)  #0.333
    a, b = Decimal("5"), Decimal("7")
    print(a / b)    #0.714
    # print(Decimal("5")/Decimal("7"))
    '''

    c = Context(prec=28, Emin=-425000000, Emax=425000000,
       ...             rounding=ROUND_HALF_EVEN, capitals=1, clamp=1,
       ...             traps=[InvalidOperation, DivisionByZero, Overflow],
       ...             flags=[])
    '''
    c=Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-425000000, Emax=425000000, capitals=1, clamp=0, flags=[],\
            traps=[InvalidOperation, DivisionByZero, Overflow])
    decimal.setcontext(c)
    print(decimal.getcontext())
    print(decimal.ExtendedContext)#normal한거 하나 보여줌... 이렇게 설정할래? 라는 느낌
    decimal.setcontext(decimal.ExtendedContext)
    print(decimal.getcontext())

if __name__ == '__main__':
    case01()


def save_config_variables(self):
        """Save the configuration variables in non-volatile memory. This method
        should be used in conjuction with *write_config_variables*.

        :rtype: boolean

        :Example:

        >>> alpha.save_config_variables()
        True
        """
        command = 0x43
        byte_list = [0x3F, 0x3C, 0x3F, 0x3C, 0x43]
        success = [0xF3, 0x43, 0x3F, 0x3C, 0x3F, 0x3C]
        resp = []

        # Send the command byte and then wait for 10 ms
        r = self.cnxn.xfer([command])[0]
        sleep(10e-3)

        # append the response of the command byte to the List
        resp.append(r)

        # Send the rest of the config bytes
        for each in byte_list:
            r = self.cnxn.xfer([each])[0]
            resp.append(r)

        sleep(0.1)

        return True if resp == success else False


from random import randint
def guess(n:int)-> int:
    pick = randint(1, 10)
    if n < pick:
        return 0
    elif n > pick:
        return -1
    else:
        return 0

def guessNumber(n:int) -> int:
    low = 1
    high = 10
    while low <= high:
        mid = (low + high) // 2
        if guess(mid) == 0:
            return mid
        elif guess(mid) == -1:
            high = mid - 1
        else:
            low = mid + 1
    return -1


#!/bin/env python3
import sys
import os
import os.path
import glob
import copy
import traceback
import time
import re
import csv
import tempfile
import urllib.request, urllib.parse, urllib.error
import shutil
import atexit
import subprocess
import time
from collections import defaultdict, Counter
from os.path import join, dirname, realpath, split
try:
    scriptdir = dirname(realpath(__file__))
except NameError:
    scriptdir = dirname(realpath(sys.argv[0]))
scriptdir1 = realpath(join(scriptdir, '..', '..', 'common', 'src'))
sys.path.append(scriptdir1)
try:
    scriptextn = "." + os.path.split(sys.argv[0])[1].rsplit('.', 1)[1]
except:
    scriptextn = ""

from execute import Execute
execprog = Execute(scriptdir,scriptdir1,extn=scriptextn)

from optparse_gui import OptionParser, OptionGroup, GUI, UserCancelledError, ProgressText
from util import *

from version import VERSION
VERSION = '%s' % (VERSION,)

def excepthook(etype, value, tb):
    traceback.print_exception(etype, value, tb)
    print("Type <Enter> to Exit...", end=' ', file=sys.stderr)
    sys.stderr.flush()
    input()
sys.excepthook = excepthook

toremove = []

def cleanup():
    for d in toremove:
        shutil.rmtree(d, ignore_errors=True)
atexit.register(cleanup)

if not GUI() and len(sys.argv) == 2 and sys.argv[1] == '--GUI':
    from optparse_gui.needswx import *
    sys.exit(1)

if GUI() and len(sys.argv) == 1:
    from optparse_gui import OptionParserGUI
    parser = OptionParserGUI(version=VERSION)
    error_kwargs = {'exit': False}
else:
    parser = OptionParser(version=VERSION)
    error_kwargs = {}

advanced = OptionGroup(parser, "Advanced")
parser.add_option("-s", "--snps", type="files", dest="snps", default=None,
                  help="Single-Nucleotide-Polymophisms. Required.", name="SNPs",
                  notNone=True, remember=True,
                  filetypes=[("SNPs", "*.vcf;*.csv;*.tsv;*.xls;*.xlsx;*.txt")])
parser.add_option("-j", "--junctions", type="files", dest="junctions", default=None,
                  help="Splice junctions. Required.", name="Splice Junctions",
                  notNone=True, remember=True,
                  filetypes=[("Splice Junctions (BED Format)", "*.bed")])
parser.add_option("-r", "--readalignments", type="files", dest="alignments", default=None,
                  help="Read alignments in BAM/SAM format. Required.", name="Read Alignments",
                  notNone=True, remember=True,
                  filetypes=[("Read Alignments (BAM/SAM Format)", "*.bam;*.sam")])
advanced.add_option("-e", "--exoncoords", type="file", dest="exoncoords", default=None,
                  help="Exon coordinates for SNV filtering. Optional.", name="Exon Coords.",
                  remember=True,
                  filetypes=[("Exonic Coordinates", "*.txt")])
advanced.add_option("-d", "--distance", type="int", dest="dist", default=50, remember=True,
                    help="Upper bound on the distance between SNP locus and splice junction. Default: 50.",
                    name="Distance Bound")
advanced.add_option("-R", "--readthrough", type="int", dest="readthrough", default=5, remember=True,
                    help="Number of bases aligning into intron. Default: 5bp.",
                    name="Read-Through")
# advanced.add_option("-M", "--matepairs", action="store_true", dest="mates", default=False, remember=True,
#                     help="Consider the mate-pair reads for the detection of splicing. Default=False.", name="Mates")
advanced.add_option("-F", "--full", action="store_true", dest="full", default=False, remember=True,
                    help="Output extra diagnostic read count fields. Default=False.", name="All Fields")
advanced.add_option("-f", "--alignmentfilter", action="store_false", dest="filter", default=True, remember=True,
                    help="(Turn off) alignment filtering by length, edits, etc.", name="Filter Alignments")
advanced.add_option("-U", "--uniquereads", action="store_true", dest="unique", default=False, remember=True,
                    help="Consider only distinct reads.", name="Unique Reads")
advanced.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, remember=True,
                    help="Quiet.", name="Quiet")
parser.add_option("-o", "--output", type="savefile", dest="output", remember=True,
                  help="Output file. Leave empty for console ouptut.", default="",
                  name="Output File", filetypes=[("All output formats", "*.xlsx;*.xls;*.csv;*.tsv;*.txt"),
                                                 ("Excel", "*.xlsx"), ("Excel2003", "*.xls"),
                                                 ("CSV", "*.csv"), ("TSV", "*.tsv"), ("Text", "*.txt")])
parser.add_option_group(advanced)

opt = None
while True:
    if 'exit' in error_kwargs:
        try:
            opt, args = parser.parse_args(opts=opt)
        except UserCancelledError:
            sys.exit(0)
    else:
        opt, args = parser.parse_args()

    break

opts.mates = False

progress = None
if not opt.output:
    opt.quiet = True
progress = ProgressText(quiet=opt.quiet)

import pysam
from dataset import XLSFileTable, CSVFileTable, TSVFileTable, XLSXFileTable, TXTFileTable, BEDFile, VCFFile

progress.stage("Read SNP data", len(opt.snps))
snpheaders = [_f for _f in """
CHROM POS REF ALT
""".split() if _f]

snvdata = {}
snvchroms = defaultdict(set)
extrasnpheaders = []
usedsnpheaders = set()
for filename in opt.snps:
    filename0 = filename
    base, extn = filename.rsplit('.', 1)
    extn = extn.lower()
    tempfilename = None
    if opt.exoncoords:
        if extn != 'vcf':
            extn = 'tsv'
        tempfileno,tempfilename = tempfile.mkstemp(suffix="."+extn)
        execprog.execute("exonicFilter", "--exons", opt.exoncoords,
                         "--input", filename, "--output", tempfilename)
        filename = tempfilename

    if extn == 'csv':
        snps = CSVFileTable(filename=filename)
    elif extn == 'vcf':
        snps = VCFFile(filename=filename)
    elif extn == 'tsv':
        snps = TSVFileTable(filename=filename)
    elif extn == 'xls':
        snps = XLSFileTable(filename=filename)
    elif extn == 'xlsx':
        snps = XLSXFileTable(filename=filename)
    elif extn == 'txt':
        snps = TXTFileTable(filename=filename, headers=snpheaders)
    else:
        raise RuntimeError("Unexpected SNP file extension: %s" % filename)

    for h in snpheaders:
        if h not in snps.headers():
            raise RuntimeError(
                "Required header: %s missing from SNP file %s" % (h, filename))

    for h in snps.headers():
        if h in snpheaders:
            continue
        if h not in extrasnpheaders:
            extrasnpheaders.append(h)

    for r in snps:
        chr = r[snpheaders[0]]
        snvchroms[filename0].add(chr)
        locus = int(r[snpheaders[1]])
        ref = r[snpheaders[2]]
        alt = r[snpheaders[3]]
        if r.get('INFO:INDEL'):
            continue
        if len(ref) != 1:
            continue
        if not re.search(r'^[ACGT](,[ACGT])*$', alt):
            continue
        for h in r:
            if r.get(h):
                usedsnpheaders.add(h)
        # cannonr = (",".join(map(lambda t: "%s:%s" % t, sorted(r.items()))))
        snpkey = (filename0, chr, locus, ref, alt)
        if snpkey not in snpdata:
            snpdata[snpkey] = r

    if tempfilename != None:
        os.unlink(tempfilename)

    progress.update()
progress.done()

chrreg = ChromLabelRegistry()

for snvfile in snvchroms:
    chrreg.add_labels(snvfile,snvchroms[snvfile])

snpdata1 = {}
for (sf, chr, locus, ref, alt), r in snvdata.items():
    chrom = chrreg.label2chrom(sf,chr)
    assert(chrom)
    snvkey = (chrom,locus,ref,alt)
    if snvkey not in snvdata1:
        snvdata1[snvkey] = (chrom,locus,ref,alt,r)

for bamfile in opt.alignments:
    chrreg.add_bamlabels(bamfile)

progress.stage("Read splice junction data", len(opt.junctions))
juncdata = set()
juncchrom = defaultdict(set)
for juncfile in opt.junctions:
    junc = BEDFile(filename=juncfile)
    for r in junc:
        chr = r['chrom']
        juncchrom[juncfile].add(chr)
        st = int(r['chromStart'])
        ed = int(r['chromEnd'])
        bs = list(map(int, r['blockSizes'].split(',')))
        assert(len(bs) == 2)
        gap = (st + bs[0], ed - bs[1])
        key = (juncfile, chr, gap)
        juncdata.add(key)
    progress.update()
progress.done()

for juncfile in juncchroms:
    chrreg.add_labels(juncfile,juncchroms[juncfile])

juncdata1 = set()
for jf, chr, gap in juncdata:
    chrom = chrreg.label2chrom(jf,chr)
    juncdata1.add((chrom, gap))

chrreg.determine_chrom_order()

snpdata = sorted(list(snvdata1.values()),key=lambda s: (chrreg.chrom_order(s[0]),s[1],s[2],s[3]))

extrasnpheaders = [h for h in extrasnpheaders if h in usedsnpheaders]
progress.message("SNPs: %d" % len(snpdata))

juncdata = sorted(((chr, gap[i], gap) for i in (0, 1) for chr, gap in juncdata1), key=lambda j: (chrreg.chrom_order(j[0]),j[1],j[2]))
progress.message("Exon/Intron junctions: %d" % len(juncdata))

outheaders = snpheaders + [_f for _f in """
NumofJuncs
Distance
Junctions
SNPJuncIntronCount
SNPJuncNoIntronCount
NoSNPJuncIntronCount
NoSNPJuncNoIntronCount
Probability
LOD
P-Value
Bonferroni
FDR
%BadRead
""".split() if _f]

debugging = [_f for _f in """
SNPMateCount
NoSNPMateCount
SNPCount
NoSNPCount
MatesCount
NotMatesCount
IntronCount
NoIntronCount
SpanningReads
RemovedDuplicateReads
FilteredReads
SNPLociReads
""".split() if _f]
debugging.extend(sorted(BadRead.allheaders))

outheaders.extend(debugging)

pos = outheaders.index("NumofJuncs")
for h in reversed(extrasnpheaders):
    outheaders.insert(pos, h)

outheaders1 = copy.copy(outheaders)
if not opt.full:
    for dh in debugging:
        if dh in outheaders1:
            outheaders1.remove(dh)

if not opt.mates:
    for mh in "SNPMateCount NoSNPMateCount MatesCount NotMatesCount".split():
        if mh in outheaders1:
            outheaders1.remove(mh)

emptysym = None
if opt.output:
    filename = opt.output
    base, extn = filename.rsplit('.', 1)
    extn = extn.lower()
    if extn == 'csv':
        output = CSVFileTable(filename=filename, headers=outheaders1)
    elif extn == 'tsv':
        output = TSVFileTable(filename=filename, headers=outheaders1)
    elif extn == 'xls':
        output = XLSFileTable(
            filename=filename, headers=outheaders1, sheet='Results')
    elif extn == 'xlsx':
        output = XLSXFileTable(
            filename=filename, headers=outheaders1, sheet='Results')
    elif extn == 'txt':
        output = TXTFileTable(filename=filename, headers=outheaders1)
    else:
        raise RuntimeError("Unexpected output file extension: %s" % filename)
else:
    output = TXTFileTable(filename=sys.stdout, headers=outheaders1)
    emptysym = "-"

outrows = []

from fisher import fisher_exact, bonferroni, fdr, lod
pvalues = []
if opt.filter:
    alifilter = SNPPileupReadFilter(maxsegments=2)
    matefilter = ReadFilter(maxsegments=2)
else:
    alifilter = BasicFilter()
    matefilter = BasicFilter()

if opt.tpb == 0:
    pileups = SerialPileups(snpdata, opt.alignments, alifilter, chrreg, matedist=opt.dist).iterator()
else:
    pileups = MultiprocPileups(snpdata, opt.alignments, alifilter, chrreg, procsperbam=opt.tpb, matedist=opt.dist).iterator()

juncindst = 0
progress.stage("Count reads per SNP and splice junction", len(snpdata))
for snpchr, snppos, ref, alt, snpextra in snpdata:

    if juncindst < len(juncdata):
        juncchr, juncpos, (inst, ined) = juncdata[juncindst]
        while (juncchr < snpchr) or ((juncchr == snpchr) and (juncpos < (snppos - opt.dist))):
            juncindst += 1
            if juncindst >= len(juncdata):
                break
            juncchr, juncpos, (inst, ined) = juncdata[juncindst]

    juncinded = juncindst

    if juncinded < len(juncdata):
        juncchr, juncpos, (inst, ined) = juncdata[juncinded]
        while (juncchr < snpchr) or ((juncchr == snpchr) and (juncpos < (snppos + opt.dist))):
            juncinded += 1
            if juncinded >= len(juncdata):
                break
            juncchr, juncpos, (inst, ined) = juncdata[juncinded]

    njunc = (juncinded - juncindst)

    snvchr1, snvpos1, ref1, alt1, total, reads, badread = next(pileups)
    assert(snvchr == snvchr1 and snvpos == snvpos1)

    reads = []
    total = 0
    badread = Counter()
    if njunc > 0:
        snppos1 = snppos - 1
        for i, samfile in enumerate(samfiles):
            for pileupcolumn in samfile.pileup(snpchr, snppos1, snppos1 + 1, truncate=True):
                total += pileupcolumn.n
                for pileupread in pileupcolumn.pileups:
                    try:
                        al, pos, base, nseg = alifilter.test(pileupread)
                    except BadRead as e:
                        badread[e.message] += 1
                        continue
                    reads.append((al, pos, base, i, nseg, None))
                    badread['Good'] += 1

    if opt.mates:
        matedreads = []
        for r in reads:
            read, qpos, base, sind, nseg, dummy = r
            if read.is_paired and not read.mate_is_unmapped and nseg == 1 and \
                    read.tid == read.rnext and abs(read.pos - read.pnext) < opt.dist - read.qlen:
                mate = samfiles[sind].mate(read)
                try:
                    matefilter.test(mate)
                    matedreads.append((read, qpos, base, sind, nseg, mate))
                except BadRead as e:
                    matedreads.append((read, qpos, base, sind, nseg, None))
            else:
                matedreads.append((read, qpos, base, sind, nseg, None))
        reads = matedreads

    any = False
    for juncind in range(juncindst, juncinded):
        juncchr, juncpos, (inst, ined) = juncdata[juncind]

        # If intron is to the left of the SNP, we require the query
        # to read into (or over) the intron by some number of bases
        goodreads = defaultdict(list)
        if juncpos == ined:
            for al, pos, base, si, ns, mal in reads:
                if (ined + opt.readthrough) < al.aend and (al.aend - al.qlen) < (ined - opt.readthrough):
                    goodreads[base].append((si, al, False))
                elif mal and (ined + opt.readthrough) < mal.aend and (mal.aend - mal.qlen) < (ined - opt.readthrough):
                    goodreads[base].append((si, mal, True))
        elif juncpos == inst:
            for al, pos, base, si, ns, mal in reads:
                if al.pos < (inst - opt.readthrough) and (inst + opt.readthrough) < (al.pos + al.qlen):
                    goodreads[base].append((si, al, False))
                elif mal and mal.pos < (inst - opt.readthrough) and (inst + opt.readthrough) < (mal.pos + mal.qlen):
                    goodreads[base].append((si, mal, True))

        if len(goodreads) == 0:
            continue

        any = True

        # Deduplicate the reads based on the read sequence or the
        # start and end of the alignment or ???
        duplicates_removed = 0
        if opt.unique:
            for base in goodreads:
                seen = set()
                retain = list()
                for si, al, mate in goodreads[base]:
                    if (si, al.seq) not in seen:
                        retain.append((si, al, mate))
                        seen.add((si, al.seq))
                    else:
                        duplicates_removed += 1
                goodreads[base] = retain

        # goodreads now contains the relevant read alignments.
        # detect read-through of splice site by checking the
        # alignment span on the reference.
        # if len(goodreads[ref]) < 5 or len(goodreads[alt]) < 5:
        #     continue
        spliced = 'SPLICED'
        notspliced = 'NOTSPLICED'
        other = 'OTHER'
        all = 'ALL'
        mates = 'MATES'
        notmates = 'NOTMATES'
        counts = defaultdict(int)
        for base in goodreads:
            for si, al, mate in goodreads[base]:
                if abs(al.alen - al.qlen) < 2:
                    counts[(base, notspliced)] += 1
                elif abs(al.alen - al.qlen - (ined - inst)) < 2:
                    counts[(base, spliced)] += 1
                else:
                    counts[(base, other)] += 1
                if mate:
                    counts[(base, mates)] += 1
                else:
                    counts[(base, notmates)] += 1
            counts[(base, all)] = counts[(base, notspliced)] + \
                counts[(base, spliced)] + \
                counts[(base, other)]
            counts[notspliced] += counts[(base, notspliced)]
            counts[spliced] += counts[(base, spliced)]
            counts[other] += counts[(base, other)]
            counts[all] += counts[(base, all)]
            counts[mates] += counts[(base, mates)]
            counts[notmates] += counts[(base, notmates)]

        nsnpi = sum([counts[(nuc, notspliced)] for nuc in alt.split(',')])
        nsnpex = sum([counts[(nuc, spliced)] for nuc in alt.split(',')])
        nwti = counts[(ref, notspliced)]
        nwtex = counts[(ref, spliced)]
        p = emptysym
        pval = emptysym
        lodval = emptysym
        if (nsnpi + nsnpex) > 0 and \
           (nwti + nwtex) > 0 and \
           (nsnpi + nwti) > 0:

            psnp = nsnpi / float(nsnpi + nsnpex)
            pwt = nwti / float(nwti + nwtex)
            p = psnp / (psnp + pwt)

            pval = fisher_exact(nsnpi,
                                (nsnpi + nsnpex),
                                (nsnpi + nwti),
                                (nsnpi + nsnpex + nwti + nwtex))

            pvalues.append(pval)

            l = lod(nsnpi, (nsnpi + nsnpex), (nsnpi + nwti),
                    (nsnpi + nsnpex + nwti + nwtex))
            if l != None:
                lodval = l

        row = [ snpchr, snppos, ref, alt ] + \
              [ snpextra.get(k, emptysym) for k in extrasnpheaders ] + \
              [njunc,
               juncpos - snppos + 1 * (juncpos > snppos),
               "%s:%d-%d" % (juncchr, inst, ined),
               counts[(alt, notspliced)],
               counts[(alt, spliced)],
               counts[(ref, notspliced)],
               counts[(ref, spliced)],
               p,
               lodval,
               pval,
               100.0 * (total - len(reads)) / float(total),
               counts[(alt, mates)],
               counts[(ref, mates)],
               counts[(alt, all)],
               counts[(ref, all)],
               counts[mates],
               counts[notmates],
               counts[notspliced],
               counts[spliced],
               counts[all],
               duplicates_removed,
               len(reads),
               total]
        for s in sorted(BadRead.allheaders):
            row.append(badread[s])
        outrows.append(row)

    if not any:

        # ["%s:%d_%s/%s"%(snpchr,snppos,ref,alt)] \
        outrows.append([snpchr, snppos, ref, alt]
                       + [snpextra.get(k, emptysym) for k in extrasnpheaders]
                       + [0])

    progress.update()
progress.done()

progress.stage('Multiple-test correction and FDR computation')
bonf = bonferroni(pvalues)
fdr = fdr(pvalues)

i = 0
pvalpos = outheaders.index('P-Value')
for r in outrows:
    if len(r) > pvalpos:
        if r[pvalpos] != emptysym:
            r.insert(pvalpos + 1, fdr[i])
            r.insert(pvalpos + 1, bonf[i])
            i += 1
        else:
            r.insert(pvalpos + 1, emptysym)
            r.insert(pvalpos + 1, emptysym)
progress.done()

progress.stage('Output results')
output.from_rows(
    [dict(list(zip(outheaders, r + [emptysym] * 50))) for r in outrows])
progress.done()


import scipy.stats as st
from collections import defaultdict
from scipy.spatial.distance import euclidean
import numpy as np
import scipy as sp

__all__ = ['LSH']

class LSH:

    def __init__(self, bin_distance, n_estimators=10, hash_size=7):
        self.n_estimators = n_estimators
        self.hash_size = hash_size
        self.w = bin_distance
        self.bin_distance = bin_distance

    def hash(self, point):
        result = []
        point = np.append(point, 1)
        for h in self.hashes:
            result.append(tuple((np.floor(np.sum(h * point, axis=1)/self.bin_distance)).astype(int)))
        return tuple(result)

    def insert(self, point):
        for est, hsh in zip(self.estimators, self.hash(point)):
            est[hsh].append(point)

    def fit(self, X):
        self.dim = len(X[0])
        self.hashes = []
        # dicts in python are hashtables so we don't have to implement them
        self.estimators = [defaultdict(lambda:[]) for i in range(self.n_estimators)]
        bin_distance = self.bin_distance
        for j in range(self.n_estimators):
            temp = []
            self.hashes.append(temp)
            for i in range(self.hash_size):
                temp.append(np.append(st.norm(0, 1).rvs(self.dim) / np.sqrt(self.dim),
                                           st.uniform(-bin_distance, bin_distance).rvs(1)))
        for x in X:
            self.insert(x)

    def kneighbours(self, point, k):
        result = []
        for est, hsh in zip(self.estimators, self.hash(point)):
            result += est[hsh]
        result.sort(key=lambda x: euclidean(x, point))
        prev = None
        cleaned = []
        for i in range(len(result)):
            if prev is None or (prev != result[i]).any():
                cleaned.append(result[i])
            prev = result[i]
        return cleaned[:k]

if __name__ == '__main__':
    import numpy as np
    from lsh import LSH
    from scipy.stats import norm, uniform
    data = uniform(loc=0, scale=100).rvs(500 * 2).reshape((500, 2))
    index = LSH(10)
    index.fit(data)
    print(index.kneighbours(data[0], k=2))


import random
import string

def handle_type_error(funktion):
    """
    Wrapper to handle type errors
    :param funktion:
    :return: funktion/ValueError
    """
    def wrapper(self, *arg):
        try:
            res = funktion(self, *arg)
        except TypeError:
            raise ValueError('Value must be type Integer! Please input correct data!')
        return res

    return wrapper

class GroupGen:
    @handle_type_error
    def group_gene8or(self, number_of_groups: int):
        """
        The function to generate random group names
        The name contain 2 characters, hyphen, 2 numbers
        :param number_of_groups:
        :return: list of randomly generated groups
        """
        list_of_groups = [f'{random.choice(string.ascii_uppercase)}'
                          f'{random.choice(string.ascii_uppercase)}'
                          f'-{random.randint(0, 9)}{random.randint(0, 9)}'
                          for i in range(0, number_of_groups)]

        return list_of_groups

class StudentGen:
    @handle_type_error
    def student_gene8or(self, number_of_students: int):
        """
        Random Full name generator.
        It combines 10 male names 10 female names and 20 last names
        :param number_of_students:
        :return: list of names
        """
        first_name_list = ['Dominick', 'Santiago', 'Quentrell', 'Kamden', 'Branson', 'Trenton', 'Arthur', 'Cayden',
                           'Keaton', 'Tobias', 'Yesenia', 'Desiree', 'Kara', 'Melody', 'Xylina', 'Lola', 'Emilia',
                           'Uma', 'Joanna', 'Queena']
        last_name_list = ['Brown', 'Nelson', 'Bailey', 'Hernandez', 'Smith', 'Coleman', 'Torres', 'Roberts', 'Collins',
                          'Sanchez', 'Washington', 'Green', 'Morris', 'Price', 'Baker', 'James', 'Campbell', 'Stewart',
                          'Howard', 'Johnson']
        list_of_names = [f'{random.choice(first_name_list)} {random.choice(last_name_list)}'
                         for i in range(0, number_of_students)]
        return list_of_names

class CourseGen:
    def courses_list(self):
        """
        Lst of courses
        :return: courses
        """
        courses = ['math', 'biology', 'physics', 'chemistry', 'astronomy', 'computer science',
                   'programming', 'history', 'modeling', 'languages']
        return courses


from multiprocessing import Process, Queue
import queue
import time

import mods.acr122u.dehc_nfc as NFC
import mods.wedderburn_di_166.dehc_scales as Scales
import mods.zebra_ds22_reader.dehc_barcode as Barcode
import mods.zebra_zc300_printer.dehc_printer as Printer

from PIL import Image

def listPrinters():
    return Printer.listPrinters()

def  getDefaultPrinter():
    return Printer.getDefaultPrinter()

class Hardware:

    processes = []

    SCALES_EXIST = False
    NFCREADER_EXIST = False
    BARCODEREADER_EXIST = False
    PRINTER_EXIST = False

    lastNFCUID = ''

    inQueueBarcode = None
    outQueueBarcode = None

    inQueueNFC = None
    outQueueNFC = None

    inQueueScales = None
    outQueueScales = None

    inQueuePrinter = None
    outQueuePrinter = None

    def __init__(self, makeScales = False, makeBarcodeReader = False, makeNFCReader = False, makePrinter = False):

        if makeScales:
            self.inQueueScales = Queue(maxsize=1) # Only store the most recent value
            self.outQueueScales = Queue(maxsize=1) # For now, we don't need to stack up C2 messages
            self.processScales = Process(target=Scales.Scales_Worker, args=(self.outQueueScales, self.inQueueScales))
            self.processes.append(self.processScales)
        if makeNFCReader:
            self.inQueueNFC = Queue(maxsize=1) # Only store the most recent value
            self.outQueueNFC = Queue(maxsize=1) # For now, we don't need to stack up C2 messages
            self.processNFC = Process(target=NFC.NFC_Worker, args=(self.outQueueNFC, self.inQueueNFC))
            self.processes.append(self.processNFC)
        if makeBarcodeReader:
            self.inQueueBarcode = Queue(maxsize=1) # Only store the most recent value
            self.outQueueBarcode = Queue(maxsize=1) # For now, we don't need to stack up C2 messages
            self.processBarcode = Process(target=Barcode.Barcode_Worker, args=(self.outQueueBarcode, self.inQueueBarcode))
            self.processes.append(self.processBarcode)
        if makePrinter:
            self.inQueuePrinter = Queue(maxsize=1) # Only store the most recent value
            self.outQueuePrinter = Queue(maxsize=1000) # For now, we don't need to stack up C2 messages
            self.processPrinter = Process(target=Printer.Printer_Worker, args=(self.outQueuePrinter, self.inQueuePrinter))
            self.processes.append(self.processPrinter)

        self.startProcesses()

        self.SCALES_EXIST = makeScales
        self.NFCREADER_EXIST = makeNFCReader
        self.BARCODEREADER_EXIST = makeBarcodeReader
        self.PRINTER_EXIST = makePrinter

    def startProcesses(self):
        for process in self.processes:
            if process is None:
                continue
            process.start()

    def terminateProcesses(self):
        if self.outQueueScales is not None:
            self.outQueueScales.put({"message": "close"})
        if self.outQueueNFC is not None:
            self.outQueueNFC.put({"message": "close"})
        if self.outQueueBarcode:
            self.outQueueBarcode.put({"message": "close"})
        if self.outQueuePrinter:
            self.outQueuePrinter.put({"message": "close"})

        time.sleep(1) # Allow some time for process to do cleanup

        for process in self.processes:
            if process is None:
                continue
            if process._popen is not None:
                process.join(1)
                process.terminate()

    def getCurrentWeight(self):
        if not self.SCALES_EXIST:
            self.lastWeight = ''
        else:
            try:
                tmpData = self.inQueueScales.get(timeout=0.01)
                if 'weight' in tmpData:
                    self.lastWeight = tmpData['weight']
            except queue.Empty:
                self.lastWeight = ''
        return self.lastWeight

    def getCurrentNFCUID(self):
        if not self.NFCREADER_EXIST:
            self.lastNFCUID = ''
        else:
            try:
                tmpData = self.inQueueNFC.get(timeout=0.01)
                if 'uid' in tmpData:
                    self.lastNFCUID = tmpData['uid']
            except queue.Empty:
                self.lastNFCUID = ''
        return self.lastNFCUID

    def getCurrentBarcode(self):
        if not self.BARCODEREADER_EXIST:
            self.lastBarcode = ''
        else:
            try:
                tmpData = self.inQueueBarcode.get(timeout=0.01)
                if 'barcode' in tmpData:
                    self.lastBarcode = tmpData['barcode']
            except queue.Empty:
                self.lastBarcode = ''
        return self.lastBarcode

    def sendNewIDCard(self, idCardImage: Image, printer: str):
        self.outQueuePrinter.put({"message": "idcard", "idcard": idCardImage, "printer": printer})

if __name__ == "__main__":

    hardware = Hardware(makeScales=True,makeBarcodeReader=True, makeNFCReader=True, makePrinter=True)

    time.sleep(1)

    weight = hardware.getCurrentWeight()

    # barcode = hardware.getCurrentBarcode()

    # nfcUID = hardware.getCurrentNFCUID()

    # for _ in range(0,1):
    #     tmpIDCard = Image.new('RGB', (400,640), (random.randint(0,255),random.randint(0,255),random.randint(0,255)))
    #     hardware.sendNewIDCard(tmpIDCard)

    print(f'Weight: {weight}')
    # print(f'Barcode: {barcode}')
    # print(f'NFC UID: {nfcUID}')

    time.sleep(1)

    hardware.terminateProcesses()


import os
import time
import argparse
import numpy as np
import torch
import torchaudio
from torchvision import transforms

from src.datasets import get_noise_factor, BASE_DIR, AudioPadding
from src.train_unet import Net, load_model_from_checkpoint
from src.models_config import base_dict

SAMPLE_RATE = base_dict['SAMPLE_RATE']

def get_voice_noise_for_inference_bank(speaker_id=None,
                                       utterance_id=None,
                                       noise_origin=None,
                                       noise_id=None,
                                       target_snr=10):
    speakers_folder = os.path.join(BASE_DIR, 'data', 'VCTK-Corpus', 'wav48')
    noise_folder = os.path.join(BASE_DIR, 'data', 'DEMAND')

    def get_audio(base_folder, folder, sound_id):
        available_folders = os.listdir(base_folder)
        if folder is None:
            folder = np.random.choice(available_folders)
        else:
            assert folder in available_folders

        available_ids = os.listdir(os.path.join(base_folder,
                                                folder))
        if sound_id is None:
            sound_id = np.random.choice(available_ids)
        else:
            assert sound_id in available_ids

        file_sound = os.path.join(base_folder, folder, sound_id)
        waveform, sample_rate = torchaudio.load(file_sound)
        if sample_rate != base_dict['SAMPLE_RATE'] or len(waveform) != base_dict['AUDIO_LEN']:
            composed = transforms.Compose([torchaudio.transforms.Resample(orig_freq=sample_rate,
                                                                          new_freq=base_dict['SAMPLE_RATE']),
                                           AudioPadding(required_length=base_dict['AUDIO_LEN'],
                                                        # padding_tail='right'
                                                        )])
            return composed(waveform), folder, sound_id

    waveform, speaker_id, utterance_id = get_audio(speakers_folder,
                                                   folder=speaker_id,
                                                   sound_id=utterance_id)
    waveform_noise, noise_origin, noise_id = get_audio(noise_folder,
                                                       folder=noise_origin,
                                                       sound_id=noise_id)

    noise_factor = get_noise_factor(waveform,
                                    waveform_noise,
                                    target_snr=target_snr)
    waveform_noise = waveform_noise * noise_factor
    utterance_id = os.path.splitext(utterance_id)[0]
    noise_id = os.path.splitext(noise_id)[0]
    return waveform + waveform_noise, speaker_id, utterance_id, noise_origin, noise_id

def get_voice_noise_for_inference_custom(path_to_file):
    waveform, sample_rate = torchaudio.load(path_to_file)
    if sample_rate != base_dict['SAMPLE_RATE'] or len(waveform) != base_dict['AUDIO_LEN']:
        composed = transforms.Compose([torchaudio.transforms.Resample(orig_freq=sample_rate,
                                                                      new_freq=base_dict['SAMPLE_RATE']),
                                       AudioPadding(required_length=base_dict['AUDIO_LEN'],
                                                    # padding_tail='right'
                                                    )])
        return composed(waveform)
    return waveform

def model_inference(model, sound_noise_wave):
    with torch.no_grad():
        return model(sound_noise_wave)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-chp', '--checkpoint_name',
                        help='checkpoint name to load model')
    parser.add_argument('-srn', '--target_snr',
                        type=int, default=10, help='target SNR')
    parser.add_argument('-speaker_id', '--speaker_id',
                        help='speaker id (VCTK folder) if None pick random')
    parser.add_argument('-utterance_id', '--utterance_id',
                        help='voice id (VCTK/speaker_id folder) if None pick random')
    parser.add_argument('-noise_origin', '--noise_origin',
                        help='noise origin (DEMAND folder) if None pick random')
    parser.add_argument('-noise_id', '--noise_id',
                        help='noise id (DEMAND/noise_origin folder) if None pick random')
    parser.add_argument('-custom_file', '--custom_file', default=None,
                        help='path to custom file')
    args = parser.parse_args()

    model_name = args.checkpoint_name
    path_for_results = os.path.join(BASE_DIR, 'results')
    os.makedirs(path_for_results, exist_ok=True)

    model_features = int(model_name.split('_')[2])
    encoder_depth = int(model_name.split('_')[3])
    model_epoch = int(model_name.split('_')[5])
    model_pref = f'{model_features}_{encoder_depth}_{model_epoch}'

    ckp_dict = load_model_from_checkpoint(args.checkpoint_name, training=False)
    model = ckp_dict['model']

    if args.custom_file is None:
        (sound_noise_wave, speaker_id,
         utterance_id, noise_origin,
         noise_id) = get_voice_noise_for_inference_bank(speaker_id=args.speaker_id,
                                                        utterance_id=args.utterance_id,
                                                        noise_origin=args.noise_origin,
                                                        noise_id=args.noise_id,
                                                        target_snr=args.target_snr)
        init_wave_file_name = f'init_sound_{utterance_id}_{noise_origin}_{noise_id}_{args.target_snr}.wav'
        path_to_init_file = os.path.join(path_for_results,
                                         init_wave_file_name)
        torchaudio.save(path_to_init_file,
                        sound_noise_wave,
                        SAMPLE_RATE,
                        precision=32)
        print(f'Init file: {path_to_init_file}')
        procecces_wave_file_name = f'model_{model_pref}_{utterance_id}_{noise_origin}_{noise_id}_{args.target_snr}.wav'
    else:
        sound_noise_wave = get_voice_noise_for_inference_custom(args.custom_file)
        print(f'Init file: {args.custom_file}')
        custom_file_name = os.path.splitext(os.path.split(args.custom_file)[1])[0]
        procecces_wave_file_name = f'model_{model_pref}_{custom_file_name}.wav'

    start = time.time()
    estimated_sound = model_inference(model, sound_noise_wave)
    inference_time = round(time.time() - start, 3)

    path_estimated_sound = os.path.join(path_for_results,
                                        procecces_wave_file_name)
    torchaudio.save(path_estimated_sound,
                    estimated_sound,
                    SAMPLE_RATE,
                    precision=32)
    print(f'Saved to {path_estimated_sound}, inference time {inference_time} s')


#!/usr/bin/env python
# -*- coding: utf8 -*-

import os
from glob import glob
import pickle
import pandas as pd
from multiprocessing import Pool, cpu_count
import json
import collections

def loadPythonSyntaxFile(filepath):
    """Loads data stored in a text file, where each line is a python dict.

    Parameters
    ----------
    filepath : str
        Path of file to be read.

    Returns
    -------
    list
        Return a list containing all the dicts from the file.

    Raises
    ------
    FileNotFoundError
    If filepath doesn't point to a .txt-file.
    """
    if not os.path.isfile(filepath) or not filepath.lower().endswith(".txt"):
        raise FileNotFoundError("The file {} doesn't seem to exist".format(filepath))
    localNamespace = dict()
    with open(filepath) as fid:
        cl0 = fid.read()
        cl1 = cl0.replace("\n{", ",\n{")  # Add "," to seperate dicts
        cl2 = cl1.strip()
        cl3 = "data = [" + cl2 + "]"  # Put all lines into a list.
        # set_trace()
        exec(cl3, localNamespace)  # Execute the python command
        return localNamespace["data"]  # Return values from localNamespace-dict

def loadUser2(user, datapath='/lscr_paper/allan/telephone',
              dataFilter=('sms', 'question', 'gps', 'bluetooth', 'screen', 'facebook', 'call')):
    """Loads a users data as dict.

    Parameters
    ----------
    user : str
        Name of user data folder
    datapath : str, optional
        Path to folder which contains user data folder.
    dataFilter : Iterable containing str, optional
        Only return certain datasets from a user. Allowed values are 'sms', 'question',
        'gps', 'bluetooth', 'screen', 'facebook' and 'call'.

    Returns
    -------
    dict
        User data in a dict, maps to None for missing data types.

    Raises
    ------
    ValueError
    If a wrong parameter is passed to dataFilter
    """
    userPath = os.path.join(datapath, user)
    userDict = dict()
    loadedFilekeySet = set()
    datafileList = os.listdir(userPath)

    # Check that dataFilter arguments are valid, raise ValueError if they aren't
    validFilterSet = {'sms', 'question', 'gps', 'bluetooth', 'screen', 'facebook', 'call'}
    if any({el not in validFilterSet for el in dataFilter}):
        raise ValueError("Invalied filter argument provided. Allowed values are %r"
                         % validFilterSet)

    for filename in datafileList:
        filekey = filename.split('.')[0]
        if filekey not in dataFilter:
            continue
        with open(os.path.join(userPath, filename)) as fid:
            userDict.update(json.load(fid))
        loadedFilekeySet.add(filekey)
    for missingFileKey in (set(dataFilter) - loadedFilekeySet):
        userDict[missingFileKey] = None
    return userDict if userDict else None  # Return None if there's no contents in userDict

def loadUser(user, datapath='/lscr_paper/allan/data/Telefon/userfiles',
             dataFilter=('sms', 'question', 'gps', 'bluetooth', 'screen', 'facebook', 'call')):
    """Loads a users data as dict.

    Parameters
    ----------
    user : str
        Name of user data folder
    datapath : str, optional
        Path to folder which contains user data folder.
    dataFilter : Iterable containing str, optional
        Only return certain datasets from a user. Allowed values are 'sms', 'question',
        'gps', 'bluetooth', 'screen', 'facebook' and 'call'.

    Returns
    -------
    dict
        User data in a dict, maps to None for missing data types.

    Raises
    ------
    ValueError
    If a wrong parameter is passed to dataFilter
    """
    # Turn "/foo/bar/baz/gps_log.txt" -> "gps"
    _filepath2dictkey = lambda el: el.rsplit(os.path.sep, maxsplit=1)[1].split("_")[0]
    userPath = os.path.join(datapath, user)
    userDict = dict()
    loadedFilekeySet = set()
    datafileList = glob(os.path.join(userPath, "*.txt"))

    # Check that dataFilter arguments are valid, raise ValueError if they aren't
    validFilterSet = {'sms', 'question', 'gps', 'bluetooth', 'screen', 'facebook', 'call'}
    if any({el not in validFilterSet for el in dataFilter}):
        raise ValueError("Invalied filter argument provided. Allowed values are %r"
                         % validFilterSet)

    for filepath in datafileList:
        dictkey = _filepath2dictkey(filepath)
        if dictkey not in dataFilter:
            continue
        userDict[dictkey] = loadPythonSyntaxFile(filepath)
        loadedFilekeySet.add(dictkey)
    for missingFileKey in (set(dataFilter) - loadedFilekeySet):
        userDict[missingFileKey] = None
    return userDict if userDict else None  # Return None if there's no contents in userDict

def _loadUserHandler(userSpec):
    """
    Helper function for loadUserParallel.
    """
    if len(userSpec) == 2:
        user, alias = userSpec
        return (alias, loadUser2(user))
    else:
        user, alias, dataFilter = userSpec
        return (alias, loadUser2(user, dataFilter=dataFilter))

def loadUserParallel(userSpec, n=None):
    """Loads users in parallel.

    Parameters
    ----------
    userSpec : tuple
        Contains (username, useralias) or (username, useralias, dataFilter).
        See the documentation for loadUser2 (which uses theese arguments).
    n : None, optional
        Number of provessor cores to use when loading the users in parallel.
        Default is 16, but will fall back to number of cores minus 1 if 16 cores
        aren't avaiable.

    Returns
    -------
    dict
        Dictionary representation of all users. Can easily be converted to pandas DataFrame.
    """
    if n is None:
        n = 16 if 16 < cpu_count() else cpu_count() - 1
    pool = Pool(n)
    users = None
    try:
        users = pool.map(_loadUserHandler, userSpec)
    finally:
        pool.terminate()
    return dict(users)

def loadUserPhonenumberDict(useralias=None, filepath="/lscr_paper/allan/phonenumbers.p"):
    """Loads the dictionary which relates a phone number to a user.
    Format is phoneID -> userID

    Parameters
    ----------
    useralias : Useralias, optional
        An Useralias instance, may be None.
    filepath : str, optional
        Path to the phonenumbers.p pickle-file.

    Returns
    -------
    dict
        phoneID -> userID
    """
    with open(filepath, "rb") as fid:
        data = pickle.load(fid)
        if useralias is not None:
            try:
                return {k: useralias[v] for (k, v) in data.items()}
            except KeyError:
                return {useralias[v]: k for (k, v) in data.items()}
        return data

def getUserList(datapath='/lscr_paper/allan/data/Telefon/userfiles'):
    """Get list of users conforming to the standard naming convention at a given path.

    Parameters
    ----------
    datapath : str, optional
        Path to search for users.

    Returns
    -------
    list
        List of valid usernames.
    """
    folders = (pth for pth in os.listdir(datapath) if os.path.isdir(os.path.join(datapath, pth)))
    userFolders = (fld for fld in folders if (len(fld) == 30))
    validUsernames = [fld for fld in userFolders if not (set(fld) - set('0123456789abcdef'))]
    return validUsernames

class Useralias(object):
    """Class used to rename user from the human ureadable hash values.
    Works like a dict for lookups, but return a new (sequentially generetad) alias for
    unknown users, whereas previous look-up users aliases is saved, and returned when
    asked for.

    Attributes
    ----------
    formatStr : str
        String template for returned aliases. Must be compatible with
    i : int
        Holds value for the next user in the sequence
    userdct : dict
        Holds previously seen hash -> alias pairs the .format()-method
    """

    def __init__(self, formatStr="u{:04d}"):
        super(Useralias, self).__init__()
        self.formatStr = formatStr
        self._json_file_path = '/lscr_paper/allan/allan_data/user_aliases.json'
        if not os.path.isfile(self._json_file_path):
            raise FileNotFoundError("The file user_aliases.json could not be found!")
        with open(self._json_file_path) as fid:
            self.userdct = json.load(fid)
        self.i = len(self.userdct)
        self.reversed = dict()

    def __setitem__(self, key, value):
        raise IndexError("The username aliases are frozen!")

    def __getitem__(self, key):
        return self.userdct[key]

    def lookup(self, alias):
        """Reverse lookup: given a useralias, return the username (hash-like string)

        Parameters
        ----------
        alias : str
            useralias, default on the form u0001, u0123, u0435, u1023 and so on....

        Returns
        -------
        dict
            Original username (hash-like), that is, the name of the user data-folder.
        """
        if len(self.reversed) != len(self.userdct):
            self.reversed = {v: k for (k, v) in self.userdct.items()}
        return self.reversed[alias]

def dict2DataFrame(dct, useralias):
    """Convert the dict-based output from loadUser to a DataFrame

    Parameters
    ----------
    dct : dict
        A single communication typy dict from a user, as returned by loadUser.
    useralias : Useralias
        A Useralias-instance or a dict mapping hashlike usernames to a human
        readable format.

    Returns
    -------
    DataFrame
        Pandas DataFrame with 'user' and 'comtype' as the index, and columns:
        - body: Hash of SMS body, NaN for calls
        - duration: Duration of call, NaN for SMS
        - number: Hash of recieving number
        - timestamp: Datetime for when the event occured
        - weekday: Weekday on which the event occured
        - hour: Hour of the day when the envent occured
    """
    df = pd.DataFrame(dct)
    df.rename(columns={'timestamp': 'timeint'}, inplace=True)
    df['timestamp'] = df.timeint.astype('datetime64[s]')  # convert Unix integer timestamps to datetime-objects
    df["weekday"] = df.timestamp.dt.weekday  # Add weekday column
    df["hour"] = df.timestamp.dt.hour  # Add hour column
    df.user = df.user.apply(lambda x: useralias[x])  # convert hex-usernames to enumerated usernames
    # Set comtype and rename 'address' to 'number' for datatype 'sms'
    if 'address' in df.columns:
        df["comtype"] = "sms"  # create the column "comtype", set it to "sms"
        df.rename(columns={'address': 'number'}, inplace=True)
    else:
        df["comtype"] = "call"
    df = df.set_index(["user", "comtype"])  # move columns "user" and "comtype" to (multi)index
    return df

def _user2DataFrameHandler(args):
    """Helper function for users2DataFrame"""
    return dict2DataFrame(*args)

def users2DataFrame(dct, useralias, n=None):
    """Convert a dict of users into a DataFrame using multiple CPU cores.

    Parameters
    ----------
    dct : dict
        Dict of users like the one returned by loadUserParallel.
    useralias : Useralias
        An Useralias-instance.
    n : None, optional
        Number of CPU cores to use.
        Default is 16, but will fall back to number of cores minus 1 if 16 cores
        aren't avaiable.

    Returns
    -------
    DataFrame
        All the users from dct as a DataFrame, as the one returned from dict2DataFrame.
    """
    if n is None:
        n = 16 if 16 < cpu_count() else cpu_count() - 1
    gen = ((comDct, useralias) for user in dct.values()
                      for comDct in user.values() if comDct)  # noqa
    try:
        pool = Pool(processes=n)
        call = pool.map_async(_user2DataFrameHandler, gen)
        call.wait()
        toConcatenate = call.get()
    finally:
        pool.terminate()
    df = pd.concat(toConcatenate)
    df.sortlevel(level=0, inplace=True)
    return df

def quickSaveHdf5(filepath, *data):
    """Given a path to a HDF5 file, save data to file.
    Data is converted to a pandas Series or DataFrame if it's too high dimmensioal for
    a Series to be used.

    Parameters
    ----------
    filepath : str
        Path to HDF5 file.
    *data
        Data to be saved. Can either be a dict or two iterables containing keys and the
        data to save.

    Raises
    ------
    ValueError
    If *data input is not correct.
    """

    def saveElement(key, data, store):
        """Save 'data' to HDF5 'store' with 'key'.
        Tries to convert everything to a Pandas Series or DataFrame if it's too
        high dimmensioal.

        Parameters
        ----------
        key : str
            Key to save data under.
        data
            Data to save.
        store : HDFStore
            The HDFStore in which the file is saved.
        """
        if not isinstance(data, (pd.DataFrame, pd.Series)):
            try:
                data = pd.Series(data)
                print("Attention! The data {} was convert to a Series".format(key))
            except Exception:
                data = pd.DataFrame(data)
                print("Attention! The data {} was convert to a DataFrame".format(key))
        store[key] = data

    with pd.HDFStore(filepath, mode='w') as store:
        if len(data) == 1 and isinstance(data[0], dict):
            for key, value in data[0].items():
                saveElement(key, value, store)
        # It's two iterables of equal length, containing the data names and the data
        elif all( len(data) == 2,  # noqa
                  all([isinstance(data[i], collections.Iterable) for i in range(2)]),
                  len(data[0] == len(data[1])) ):  # noqa
            for key, value in data:
                saveElement(key, value, store)
        else:
            raise ValueError("Incorrect data input.")

def loadUserBluetooth(userhash, useralias):
    """Function for loading an users bluetooth data.

    Parameters
    ----------
    userhash : str
        Hash value for user (username)
    useralias : Useralias
        Useralias instance.

    Returns
    -------
    pd.DataFrame
        DataFrame with users Bluetooth data.
    """
    def _load_bluetooth(user):
        try:
            with open(f'/lscr_paper/allan/telephone/{user}/bluetooth.json') as fid:
                data = json.load(fid)
            return data['bluetooth']
        except Exception as err:
            print(f"Couldn't read user {user}")
            raise err

    btdata = _load_bluetooth(userhash)
    if not btdata:
        return None
    df = pd.DataFrame(btdata)
    df['timestamp'] = df.timestamp.astype('datetime64[s]')
    df = df[df.bt_mac != '-1']
    # ua = loaders.Useralias()
    if isinstance(useralias, dict):
        df['scanned_user'] = df.scanned_user.map(useralias)
        df['user'] = df.user.map(useralias)
    else:
        df['scanned_user'] = df.scanned_user.map(useralias.userdct)
        df['user'] = df.user.map(useralias.userdct)
    df = df.set_index('timestamp')
    df = df[df.index.year >= 2013]
    return df


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Labtest.py
    this is a test run for screens and stuff
    primer for pygame out prior to using it elsewhere.

References:
 https://realpython.com/pygame-a-primer/
 https://github.com/realpython/pygame-primer
 https://github.com/sagardspeed2/PrimerGame
 https://stackoverflow.com/questions/18067219/pygame-image-screen-fill

"""

__author__ = "Denis J Jackman (denis_jackman@hotmail.com)"
__version__ = "$Revision: 1.0 $"
__date__ = "$Date: 2022/07/30 11:31:00 $"
__copyright__ = "Copyright (c) 2022 Denis J Jackman"
__license__ = "Python"

from djgamemodule import colours as col
import pygame

WIDTH = 800
HEIGHT = 600

def create_surface(surface_colour, surface_size):
    '''
        surface create function
        take a surface colour (RGB code)
        take a size (width , height)
        create and return a surface
    '''
    result_surface = pygame.Surface(surface_size)  # Create empty pygame surface
    result_surface.fill(surface_colour)              # Fill the background white color (red,green,blue)
    result_surface = result_surface.convert()       # Convert Surface to make blitting faster
    return result_surface

def title_work():
    '''
        do everything needed for the tile
    '''
    font_title = pygame.font.SysFont(None, 48)
    title_work_result = create_surface(col.RED, (800,50))
    titlefont = font_title.render('Game Title', True, col.BLACK)
    xwidth = (title_work_result.get_width() / 2) - (titlefont.get_width() / 2)
    yheight = (title_work_result.get_height() / 2) - (titlefont.get_height() / 2)

    title_work_result.blit(titlefont, (xwidth, yheight))
    return title_work_result

def log_messages():
    '''
        do everything needed for the tile
    '''
    font_log = pygame.font.SysFont(None, 24)
    log_work_result = create_surface(col.DIM_GRAY, (800,50))
    logfont = font_log.render('Message status', True, col.BLACK)
    log_work_result.blit(logfont, (0,0))
    return log_work_result

def labtest_main():
    '''
        labtest main function
    '''
    pygame.init()

    screen = pygame.display.set_mode((WIDTH, HEIGHT))

    runtest = True
    while runtest:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                runtest = False
        screen.fill((col.WHITE))

        background = create_surface(col.BLACK, (800,600))
        gamearea = create_surface(col.DARK_GREEN, (700,500))
        stats = create_surface(col.BLUE, (100,500))

        screen.blit(background, (0, 0))
        screen.blit(title_work(), (0, 0))
        screen.blit(gamearea, (0, 50))
        screen.blit(stats, (700, 50))
        screen.blit(log_messages(), (0,550))

        pygame.display.flip()

# Main loop for the game
if __name__ == '__main__':
    labtest_main()


import pika
from pika.exchange_type import ExchangeType

def alt_queue_on_message_received(ch, method, properties, body):
    print(f"Alt: received new message: {body}")

def main_queue_on_message_received(ch, method, properties, body):
    print(f"Main: received new message: {body}")

connection_parameters = pika.ConnectionParameters('localhost')
connection = pika.BlockingConnection(connection_parameters)

channel = connection.channel()

# alternative exchange queue
channel.exchange_declare(exchange='alt_exchange', exchange_type=ExchangeType.fanout)
channel.queue_declare(queue='alt_exchange_queue')
channel.queue_bind('alt_exchange_queue', 'alt_exchange')
channel.basic_consume(queue='alt_exchange_queue', auto_ack=True, on_message_callback=alt_queue_on_message_received)

# main exchange queue
channel.exchange_declare(exchange='main_exchange', exchange_type=ExchangeType.direct,
                         arguments={'alternate-exchange': 'alt_exchange'})
channel.queue_declare(queue='main_exchange_queue')
channel.queue_bind('main_exchange_queue', 'main_exchange', routing_key='test')
channel.basic_consume(queue='main_exchange_queue', auto_ack=True, on_message_callback=main_queue_on_message_received)

print("Starting Consuming")
channel.start_consuming()


import numpy as np

from numba import njit, f8, i8, b1
from numba.pycc import CC

# temporary name for compiled module
cc = CC("solution_HIadj_pre_anthesis")

@cc.export("HIadj_pre_anthesis", (f8,f8,f8,f8))
def HIadj_pre_anthesis(
    NewCond_B: float,
    NewCond_B_NS: float,
    NewCond_CC: float,
    Crop_dHI_pre: float
    ) -> float:
    """
    Function to calculate adjustment to harvest index for pre-anthesis water
    stress

    Reference Manual: harvest index calculations (pg. 110-126)

    Arguments:

        NewCond_B (float): biomass growth

        NewCond_B_NS (float): biomass growth (no stress)

        NewCond_CC (float): canopy cover

        Crop_dHI_pre (float): Crop_dHI_pre

    Returns:

        NewCond_Fpre (float): adjustment to harvest index for pre-anthesis water stress

    """

    ## Store initial conditions in structure for updating ##
    # NewCond = InitCond

    # check that there is an adjustment to be made
    if Crop_dHI_pre > 0:
        ## Calculate adjustment ##
        # Get parameters
        Br = NewCond_B / NewCond_B_NS
        Br_range = np.log(Crop_dHI_pre) / 5.62
        Br_upp = 1
        Br_low = 1 - Br_range
        Br_top = Br_upp - (Br_range / 3)

        # Get biomass ratios
        ratio_low = (Br - Br_low) / (Br_top - Br_low)
        ratio_upp = (Br - Br_top) / (Br_upp - Br_top)

        # Calculate adjustment factor
        if (Br >= Br_low) and (Br < Br_top):
            NewCond_Fpre = 1 + (
                ((1 + np.sin((1.5 - ratio_low) * np.pi)) / 2) * (Crop_dHI_pre / 100)
            )
        elif (Br > Br_top) and (Br <= Br_upp):
            NewCond_Fpre = 1 + (
                ((1 + np.sin((0.5 + ratio_upp) * np.pi)) / 2) * (Crop_dHI_pre / 100)
            )
        else:
            NewCond_Fpre = 1
    else:
        NewCond_Fpre = 1

    if NewCond_CC <= 0.01:
        # No green canopy cover left at start of flowering so no harvestable
        # crop will develop
        NewCond_Fpre = 0

    return NewCond_Fpre

if __name__ == "__main__":
    cc.compile()


"""Tests for Widget."""
import os
import shutil
from pathlib import Path
from unittest import TestCase

from ipywidgets import widgets
from qiskit.circuit.random import random_circuit
from qiskit.primitives import Estimator
from qiskit.quantum_info.random import random_pauli

from purplecaffeine.core import LocalStorage, Trial
from purplecaffeine.widget import Widget

class TestWidget(TestCase):
    """TestTrial."""

    def setUp(self) -> None:
        """SetUp local storage backend for widget use"""
        curr_dir = os.path.dirname(os.path.abspath(__file__))
        self.save_path = os.path.join(curr_dir, "test")
        if not os.path.exists(self.save_path):
            Path(self.save_path).mkdir(parents=True, exist_ok=True)
        self.local_storage = LocalStorage(path=self.save_path)

    def test_render_pagination(self):
        """Test to check that pagination buttons work correctly"""
        self.add_n_trials(20)
        widget = Widget(self.local_storage)
        box = widget.render_pagination()
        previous_button = box.children[0]
        next_button = box.children[1]
        self.assertFalse(next_button.disabled)
        self.assertTrue(previous_button.disabled)

    def test_render_trial(self):
        """Test to check that trails buttons are being displayed"""
        self.add_n_trials(5)
        widget = Widget(self.local_storage)
        tabs = widget.render_trial()
        info = tabs.children[0]
        self.assertTrue(self.local_storage.list()[0].uuid in info.value)

    def test_search(self):
        """Test to check the search function"""
        self.add_n_trials(20)
        widget = Widget(self.local_storage)
        box = widget.search()
        search_button = box.children[0]
        input_text = box.children[1]
        input_text.value = self.local_storage.list()[0].name
        search_button.click()
        self.assertEqual(widget.search_value, self.local_storage.list()[0].name)
        self.assertEqual(len(widget.trials), 1)
        input_text.value = ""
        search_button.click()
        self.assertEqual(len(widget.trials), 10)

    def test_load_detail(self):
        """Test to check that the selected trial is the first one"""
        self.add_n_trials(2)
        widget = Widget(self.local_storage)
        first_trail = self.local_storage.list()[0]
        button = widgets.Button(
            description=first_trail.name,
            disabled=False,
            button_style="",
            tooltip=first_trail.uuid,
            icon="",
        )
        widget.load_detail(button)
        self.assertEqual(widget.selected_trial.uuid, first_trail.uuid)

    def test_trial_list(self):
        """Test to check that new trials are being added."""
        self.add_n_trials(20)
        widget = Widget(self.local_storage)
        self.assertEqual(
            widget.render_trails_list().children[0].tooltip,
            self.local_storage.list()[0].uuid,
        )
        self.assertEqual(
            widget.render_trails_list().children[0].tooltip, widget.selected_trial.uuid
        )

    def add_n_trials(self, number_of_trials):
        """Aux function to add 20 trials to the class storage"""
        n_qubits = 4
        depth = 3
        shots = 2000

        circuit = random_circuit(n_qubits, depth)
        obs = random_pauli(n_qubits)
        for i in range(0, number_of_trials):
            with Trial("Example trial " + str(i), storage=self.local_storage) as trial:
                # track some parameters
                trial.add_parameter("estimator", "qiskit.primitives.Estimator")
                trial.add_parameter("depth", str(depth))
                trial.add_parameter("n_qubits", str(n_qubits))
                trial.add_parameter("shots", str(shots))

                # track objects of interest
                trial.add_circuit("circuit", circuit)

                # run
                exp_value = (
                    Estimator().run(circuit, obs, shots=shots).result().values.item()
                )

                # track results of run
                trial.add_metric("exp_value", exp_value)

    def tearDown(self) -> None:
        """TearDown the trials objects."""
        trials_files = os.path.join(self.save_path)
        if os.path.exists(trials_files):
            shutil.rmtree(trials_files)


from tkinter import *
#from ljf import run_labjack
import datetime

from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk
from ljf import run_labjack

class LJGui:

    def __init__(self):
        #set up main window
        self.myGui = Tk()
        self.myGui.title("Balance Community LabJack Processor")
        self.myGui.geometry("450x200+600+200")
        load = Image.open("logo.png")
        load_resized = load.resize((200, 25), Image.ANTIALIAS)
        render = ImageTk.PhotoImage(load_resized)
        img = Label(self.myGui, image=render)
        img.image = render
        img.place(x=0, y=0)
        self.myLabel = Label(text="Welcome! Enter the max number of requests, directory to save to and press run:",
                             fg="blue",justify='left').place(x=5,y=30)

        #variables
        self.MAX_REQUEST= IntVar()
        self.MAX_REQUEST_OUTPUT = str(self.MAX_REQUEST)
        self.directory = StringVar()
        self.path = StringVar()
        self.output= StringVar()

        #labels
        self.finalMax = Label(textvariable = self.MAX_REQUEST_OUTPUT,fg='red',justify='left').place(x=150,y=60)
        self.finalSaveTo= Label(textvariable = self.directory,fg='red',justify='left').place(x=150,y=90)
        self.finalRun = Label(textvariable = self.path, fg = 'red',justify='left').place(x=150,y=120)

        #input boxes and buttons
        MaxEntry = Entry(self.myGui, textvariable =  self.MAX_REQUEST,justify='center').place(x=10,y=60)
        SaveToButton = Button(text = "Save to", command = self.SaveTo).place(x=10,y=90)
        RunButton = Button(text = "Run", command = self.Run).place(x=10,y=120)
        reset = Button(text = "Reset Values", command = self.reset_values).place(x=10,y=160)
        quit = Button(text="Quit", command = self.closeWin).place(x=100,y=160)

        self.myGui.mainloop()

    #functions

    def SaveTo(self):
        self.directory.set(filedialog.askdirectory(initialdir='/'))

    def Run(self):
        self.path.set(str(self.directory.get()) + '/' + datetime.datetime.now().strftime(format='%Y-%m-%d_%H-%M-%S'))
        try:
            run_labjack(self.path.get(), self.MAX_REQUEST.get())
            self.output.set("Success")
        except Exception as e:
            self.output.set(str(e))

    def closeWin(self):
        self.myGui.destroy()                   #Close Window Function

    def reset_values(self):
        self.MAX_REQUEST.set(0)
        self.directory.set('')
        self.path.set('')
        self.output.set('')


from urllib.parse import urlencode

from book.filters import BookFilter
from book.forms import BookRentForm
from book.models import Book, BookRent, RentDayHistory

from decorators import lock_for_user

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Case, CharField, Count, F, Sum, Value, When
from django.db.models.functions import Concat
from django.http import JsonResponse
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, ListView, View

from django_filters.views import FilterView

__all__ = [
    'CreateBookRentView', 'SearchBook', 'BookRentTableBillingView',
    'BookRentTableView', 'BookList',
]

class CreateBookRentView(LoginRequiredMixin, CreateView):
    model = BookRent
    template_name = 'account/rent_create.html'
    form_class = BookRentForm
    success_url = reverse_lazy('account:profile_overall')

    @method_decorator(lock_for_user(methods=('POST',)))
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['request'] = self.request
        return kwargs

    def get_success_url(self):
        messages.add_message(self.request, messages.SUCCESS, 'Book Rent was created successfully.')
        return super().get_success_url()

class SearchBook(LoginRequiredMixin, View):
    def get(self, request):
        # wrap in list(), because QuerySet is not JSON serializable
        data = list(self.get_queryset())
        return JsonResponse(data, safe=False)

    def get_queryset(self):
        # generate human readable tariff and category name
        case = Case(
            When(
                category__days_period=0,
                then=Concat(
                    Value('Title: '),
                    F('title'),
                    Value(' $'),
                    F('category__price'),
                    Value(' each day. Category: '),
                    F('category__name'),
                )
            ),
            default=Concat(
                Value('Title: '),
                F('title'),
                Value(' $'),
                F('category__price_period'),
                Value(' for first '),
                F('category__days_period'),
                Value(' day/s. $'),
                F('category__price'),
                Value(' each day afterwards. Category: '),
                F('category__name'),
            ),
            output_field=CharField(),
        )
        values = ('id', 'title', 'price_display', 'category__name')
        limit = 10

        return Book.objects \
            .filter(title__search=self.search_term) \
            .annotate(price_display=case) \
            .values(*values)[:limit]

    @property
    def search_term(self):
        return self.request.GET.get('search') or ''

class BookRentTableBillingView(LoginRequiredMixin, ListView):
    queryset = RentDayHistory.objects.all()
    template_name = 'account/rent_list.html'

    def get_queryset(self):
        queryset = super().get_queryset()
        values = (
            'rent_id', 'rent', 'rent__book__title',
            'rent__status', 'rent__end', 'rent__book__category__name'
        )
        # generate human readable tariff
        tariff = Case(
            When(
                rent__days_period_initial=0,
                then=Concat(
                    Value('$'),
                    F('rent__price'),
                    Value(' each day.'),
                )
            ),
            default=Concat(
                Value('$'),
                F('rent__price_period'),
                Value(' for first '),
                F('rent__days_period_initial'),
                Value(' day/s. $'),
                F('rent__price'),
                Value(' each day afterwards.'),
            ),
            output_field=CharField(),
        )
        return queryset\
            .filter(rent__user_id=self.request.user.id)\
            .values(*values)\
            .annotate(
                total_amount=Sum('amount'),
                count=Count('id'),
                tariff=tariff,
            )

class BookRentTableView(LoginRequiredMixin, ListView):
    queryset = BookRent.objects.all().select_related('book')
    template_name = 'account/rent_list_table.html'

    def get_queryset(self):
        queryset = super().get_queryset()
        # generate human readable tariff and category name
        tariff = Case(
            When(
                days_period_initial=0,
                then=Concat(
                    Value('$'),
                    F('price'),
                    Value(' each day.'),
                )
            ),
            default=Concat(
                Value('$'),
                F('price_period'),
                Value(' for first '),
                F('days_period_initial'),
                Value(' day/s. $'),
                F('price'),
                Value(' each day afterwards.'),
            ),
            output_field=CharField(),
        )
        return queryset.filter(user_id=self.request.user.id).annotate(tariff=tariff)

class BookList(FilterView):
    queryset = Book.objects.all().select_related('category').order_by('-id')
    template_name = 'account/book_list.html'
    paginate_by = 12
    filterset_class = BookFilter

    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(*args, **kwargs)
        get = dict(tuple(self.request.GET.items()))
        get.pop('page', None)
        context['request_GET'] = urlencode(get)
        return context


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import asyncio

__all__ = ["BaseLooper"]

class BaseLooper(object):
    def __init__(self):
        self._running = False

    def is_running(self):
        return self._running

    async def start(self):
        """start server"""
        self._running = True
        print(type(self),"start")

    async def stop(self):
        """stop server"""
        self._running = False
        print(type(self),"stop")

    async def run(self):
        """
        execution task cycle
        """
        print(type(self),"run")


from __future__ import absolute_import, division, print_function
from scitbx import matrix
from scitbx.array_family import flex
from scitbx.math import tensor_rank_2_gradient_transform_matrix
from libtbx.test_utils import approx_equal
import math
from six.moves import cStringIO as StringIO
import sys
from six.moves import range

flex.set_random_seed(0)

mtps = -2 * math.pi**2

class debye_waller:

  def __init__(self, u, ops, hkl):
    self.u = u
    self.ops = ops
    self.hkl = hkl

  def f(self):
    result = 0
    for op in self.ops:
      op_u = (op*matrix.sym(sym_mat3=self.u)*op.transpose()).as_sym_mat3()
      huh = (matrix.row(self.hkl) \
          * matrix.sym(sym_mat3=op_u)).dot(matrix.col(self.hkl))
      result += math.exp(mtps * huh)
    return result

  def d_u(self):
    result = flex.double(6, 0)
    h,k,l = self.hkl
    d_exp_huh_d_u = matrix.col([h**2, k**2, l**2, 2*h*k, 2*h*l, 2*k*l])
    for op in self.ops:
      op_u = (op*matrix.sym(sym_mat3=self.u)*op.transpose()).as_sym_mat3()
      huh = (matrix.row(self.hkl) \
          * matrix.sym(sym_mat3=op_u)).dot(matrix.col(self.hkl))
      d_op_u = math.exp(mtps * huh) * mtps * d_exp_huh_d_u
      gtmx = tensor_rank_2_gradient_transform_matrix(op)
      d_u = gtmx.matrix_multiply(flex.double(d_op_u))
      result += d_u
    return result

  def d2_u(self):
    result = flex.double(flex.grid(6,6), 0)
    h,k,l = self.hkl
    d_exp_huh_d_u = flex.double([h**2, k**2, l**2, 2*h*k, 2*h*l, 2*k*l])
    d2_exp_huh_d_uu = d_exp_huh_d_u.matrix_outer_product(d_exp_huh_d_u)
    for op in self.ops:
      op_u = (op*matrix.sym(sym_mat3=self.u)*op.transpose()).as_sym_mat3()
      huh = (matrix.row(self.hkl) \
          * matrix.sym(sym_mat3=op_u)).dot(matrix.col(self.hkl))
      d2_op_u = math.exp(mtps * huh) * mtps**2 * d2_exp_huh_d_uu
      gtmx = tensor_rank_2_gradient_transform_matrix(op)
      d2_u = gtmx.matrix_multiply(d2_op_u).matrix_multiply(
        gtmx.matrix_transpose())
      result += d2_u
    return result

def d_debye_waller_d_u_finite(u, ops, hkl, eps=1.e-8):
  result = flex.double()
  u_eps = list(u)
  for ip in range(6):
    vs = []
    for signed_eps in [eps, -eps]:
      u_eps[ip] = u[ip] + signed_eps
      a = debye_waller(u=matrix.col(u_eps), ops=ops, hkl=hkl)
      vs.append(a.f())
    u_eps[ip] = u[ip]
    result.append((vs[0]-vs[1])/(2*eps))
  return result

def d2_debye_waller_d_u_finite(u, ops, hkl, eps=1.e-8):
  result = flex.double()
  u_eps = list(u)
  for ip in range(6):
    vs = []
    for signed_eps in [eps, -eps]:
      u_eps[ip] = u[ip] + signed_eps
      a = debye_waller(u=matrix.col(u_eps), ops=ops, hkl=hkl)
      vs.append(a.d_u())
    u_eps[ip] = u[ip]
    result.extend((vs[0]-vs[1])/(2*eps))
  return result

def compare_derivatives(ana, fin):
  s = max(1, flex.max(flex.abs(ana)))
  assert approx_equal(ana/s, fin/s)

def exercise(args):
  verbose =  "--verbose" in args
  if (not verbose):
    out = StringIO()
  else:
    out = sys.stdout
  for i_trial in range(100):
    ops = []
    for i in range(3):
      ops.append(matrix.sqr(flex.random_double(size=9, factor=4)-2))
    u = matrix.col((flex.random_double(size=6, factor=2)-1)*1.e-3)
    hkl = matrix.row(flex.random_double(size=3, factor=4)-2)
    dw = debye_waller(u=u, ops=ops, hkl=hkl)
    grads_fin = d_debye_waller_d_u_finite(u=u, ops=ops, hkl=hkl)
    print("grads_fin:", list(grads_fin), file=out)
    grads_ana = dw.d_u()
    print("grads_ana:", list(grads_ana), file=out)
    compare_derivatives(grads_ana, grads_fin)
    curvs_fin = d2_debye_waller_d_u_finite(u=u, ops=ops, hkl=hkl)
    print("curvs_fin:", list(curvs_fin), file=out)
    curvs_ana = dw.d2_u()
    print("curvs_ana:", list(curvs_ana), file=out)
    compare_derivatives(curvs_ana, curvs_fin)
    print(file=out)
  print("OK")

if (__name__ == "__main__"):
  exercise(sys.argv[1:])