import numpy as np
import matplotlib.pyplot as plt

from exp_fam_lik_normal_fixed_var import (
    calc_likelihood_sufficient_statistics_ND,
    calc_likelihood_reference_measure_N)

from exp_fam_prior_normal_fixed_var import (
    ExpFamPrior_NormalFixedVariance, calc_prior_cumulant)

from scipy.special import logsumexp

def calc_ELBO_per_token(h_N, sx_ND, resp_NK, qdist_list, prior_dist, after_mstep=True):
    _, K = resp_NK.shape
    N, D = sx_ND.shape

    stats_dict = estep__update_summary_stats_dict(sx_ND, resp_NK)

    unif_pi_K = 1.0/K * np.ones(K)
    L_alloc = (
        np.dot(stats_dict['N_K'], unif_pi_K)
        )
    L_entropy = np.sum(stats_dict['H_K'])
    L_obs = (
        np.sum(h_N)
        - K * prior_dist.calc_cumulant()
        + np.sum([q_dist_k.calc_cumulant() for q_dist_k in qdist_list])
        )
    if not after_mstep:
        for k in range(K):
            nu_diff = stats_dict['N_K'][k] + prior_dist.nu_1[0] - qdist_list[k].nu_1[0]
            tau_diff_D = stats_dict['S_KD'][k] + prior_dist.tau_D - qdist_list[k].tau_D

            L_obs += nu_diff * qdist_list[k].calc_E_negative_c_of_phi()
            L_obs += np.dot(tau_diff_D, qdist_list[k].calc_E_phi_D())

    assert np.isscalar(L_alloc)
    assert np.isscalar(L_obs)
    return (L_alloc + L_entropy + L_obs) / (N * D)

def estep__update_resp_NK(sx_ND, qdist_list):

    logpdf_x_NK = np.zeros((N, K))
    for k in range(K):
        E_phi_k_D = qdist_list[k].calc_E_phi_D()
        E_negative_cumulant_k = qdist_list[k].calc_E_negative_c_of_phi()
        logpdf_x_NK[:,k] = np.dot(sx_ND, E_phi_k_D) + E_negative_cumulant_k

    logpdf_x_NK -= logsumexp(logpdf_x_NK, axis=1)[:,np.newaxis]
    resp_NK = logpdf_x_NK
    np.exp(resp_NK, out=resp_NK)
    assert np.allclose(np.sum(resp_NK, axis=1), 1.0)
    return resp_NK

def estep__update_summary_stats_dict(sx_ND, resp_NK, stats_dict=None):
    if stats_dict is None:
        stats_dict = dict()
    stats_dict['N_K'] = np.sum(resp_NK, axis=0)
    stats_dict['S_KD'] = np.dot(resp_NK.T, sx_ND)
    stats_dict['H_K'] = -1 * np.sum(resp_NK * np.log(1e-100 + resp_NK), axis=0)
    return stats_dict

def mstep__update_q(stats_dict, prior_dist):
    qdist_list = list()
    for k in range(K):
        qdist_k = ExpFamPrior_NormalFixedVariance(
            tau_D=stats_dict['S_KD'][k] + prior_dist.tau_D,
            nu_1=stats_dict['N_K'][k] + prior_dist.nu_1,
            v_D=prior_dist.v_D,
            )
        qdist_list.append(qdist_k)
    return qdist_list

def run_coordinate_ascent(data_ND, prior_dist,
        init_resp_NK=None, K=10,
        n_iters=100, random_state=0):
    N, D = data_ND.shape

    ## Randomly assign a few data points to given clusters K
    if init_resp_NK is None:
        prng = np.random.RandomState(random_state)
        chosen_ids_K = prng.choice(np.arange(N), K)

        init_resp_NK = np.zeros((N,K))
        for k in range(K):
            init_resp_NK[chosen_ids_K[k], :] = 0.01
            init_resp_NK[chosen_ids_K[k], k] = 1.00 - 0.01 * (K-1)

    h_N = calc_likelihood_reference_measure_N(data_ND, prior_dist.v_D)
    sx_ND = calc_likelihood_sufficient_statistics_ND(data_ND, prior_dist.v_D)

    # Initialize other parameters just by running updates from the given resp
    stats_dict = estep__update_summary_stats_dict(sx_ND, init_resp_NK)
    qdist_list = mstep__update_q(stats_dict, prior_dist)

    elbo_history = list()

    for iterid in range(n_iters):

        resp_NK = estep__update_resp_NK(sx_ND, qdist_list)
        elbo = calc_ELBO_per_token(
            h_N, sx_ND, resp_NK, qdist_list, prior_dist, after_mstep=False)
        elbo_history.append(elbo)
        print("%4d/%d after estep % 11.6f" % (iterid, n_iters, elbo))
        if len(elbo_history) >= 2:
            assert (elbo_history[-1] - elbo_history[-2]) >= -1e-8

        stats_dict = estep__update_summary_stats_dict(sx_ND, resp_NK, stats_dict)
        qdist_list = mstep__update_q(stats_dict, prior_dist)
        elbo = calc_ELBO_per_token(
            h_N, sx_ND, resp_NK, qdist_list, prior_dist, after_mstep=True)
        elbo_history.append(elbo)
        print("%4d/%d after mstep % 11.6f" % (iterid, n_iters, elbo))

        if len(elbo_history) >= 2:
            assert (elbo_history[-1] - elbo_history[-2]) >= -1e-8

    return (resp_NK, stats_dict, qdist_list), elbo_history

if __name__ == '__main__':
    prng = np.random.RandomState(0)

    K = 3
    D = 2
    true_v_D = np.asarray([0.15, 0.3])

    os_KD = prng.choice(np.asarray([-1.0, +1.0]), size=K*D, replace=True).reshape((K,D))
    true_phi_KD = np.asarray([1.337, -2.0, 3.0]).reshape((K, 1)) * os_KD

    N = 3000
    data_list = list()
    for k in range(K):
        data_k_ND = (
            true_phi_KD[k][np.newaxis,:]
            + true_v_D[np.newaxis,:] * prng.randn(N//K, D))
        data_list.append(data_k_ND)
    data_ND = np.vstack(data_list)

    prior_dist = ExpFamPrior_NormalFixedVariance(
        nu_1=0.25, tau_D=1.0 * np.ones(D), v_D=true_v_D)

    elbo_history_list = list()
    config_list = list()
    for run in range(25):
        final_config, elbo_history = run_coordinate_ascent(
            data_ND, prior_dist,
            K=K,
            n_iters=50, random_state=np.random.randint(100))
        elbo_history_list.append(elbo_history)
        config_list.append(final_config)

    fig, ax = plt.subplots(nrows=1, ncols=1)
    elbo_SR = np.vstack(elbo_history_list).T
    ax.plot(elbo_SR)

    ymax = elbo_SR.max()
    ymin = elbo_SR[-1,:].min()
    R = ymax - ymin
    ax.set_ylim(ymin - R, ymax + 0.1 * R)
    ax.set_xlabel('number of update iterations')
    ax.set_ylabel('log p(x) per token')

    fig, ax = plt.subplots(nrows=5, ncols=5, sharex=True, sharey=True)
    for row in range(5):
        for col in range(5):
            ax[row, col].plot(data_ND[:,0], data_ND[:,1], 'k.', alpha=0.1)

            k = 5 * row + col
            _, _, q_list = config_list[k]
            for k in range(K):
                E_phi_k_D = q_list[k].calc_E_phi_D()
                ax[row, col].plot(E_phi_k_D[0], E_phi_k_D[1], '+')

    plt.show()


from typing import Iterable

def get_braille(s: str) -> str:
    """
    `s` specifies which dots in the desired braille character must be on ('1') and which must be off ('0').
    Dots in the 2x8 braille matrix are ordered top-down, left-to-right.
    The order of the '1's and '0's in `s` correspond to this.
    Schematic example:
    ▪    10
     ▪   01
    ▪▪ = 11
     ▪   01
    ⢵ = '10100111'

    More examples:
    '10000000' = ⠁ (only top left dot)
    '11001111' = ⢻
    '11111111' = ⣿
    '00000000' = ⠀ (empty braille character)
    """
    s = (
        s[:3] + s[4:7] + s[3] + s[7]
    )  # rearrange ISO/TR 11548-1 dot order to something more suitable
    return chr(0x2800 + int(s[::-1], 2))

def braille_bin(char: str) -> str:
    """Inverse of get_braille()"""
    o = ord(char) - 0x2800
    s = format(o, "b").rjust(8, "0")
    s = s[::-1]
    s = (
        s[:3] + s[6] + s[3:6] + s[7]
    )  # rearrange ISO/TR 11548-1 dot order to something more suitable
    return s

def is_braille(char: str) -> bool:
    """Return True if provided unicode character is a braille character."""
    return isinstance(char, str) and 0x2800 <= ord(char[0]) <= 0x28FF

def braille_from_xy(x: int, y: int) -> str:
    """
    Returns braille character with dot at x, y position filled in.
    Example: braille_from_xy(x=1, y=0) returns "⠈" (top right dot filled in)
    """
    if not 0 <= x <= 1 or not 0 <= y <= 3:
        raise ValueError("Invalid braille dot position.")
    s = ["0"] * 8
    s[x * 4 + y] = "1"
    return get_braille("".join(s))

def combine_braille(braille: Iterable[str]) -> str:
    """
    Returns braille character that combines dots of input braille characters.
    Example: combine_braille("⠁⠂") returns "⠃"
    """
    out_bin = 0b00000000
    for char in braille:
        braille_b = braille_bin(char)
        out_bin |= int(braille_b, 2)
    s = format(out_bin, "b").rjust(8, "0")
    return get_braille(s)

def draw_braille(x: float, y: float, canvas_str=None) -> str:
    """
    Returns braille character for given x, y position.
    If canvas_str is already a braille character, the new braille dot will be added to it.
    """
    x = round((x + 0.500000001) % 1)  # 0 or 1. 0.500000001 so it rounds half up.
    y = 3 - round((-y + 0.375000001) % 1 * 4) % 4  # 0, 1, 2, or 3
    out = braille_from_xy(x, y)
    for character in canvas_str:
        if is_braille(character):
            out = combine_braille([out, character])
            break
    return out


'''
    Implementation of Spell Checking

    This program implements a spell checker for the words typed out
'''

import sys
from PyQt5 import QtGui
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setGeometry(500, 500, 500, 500)

        self.setWindowTitle("Text Tools")

        #creates the central widget and text box
        self.text_box = QWidget()
        self.text_box = QTextEdit()

        self.setCentralWidget(self.text_box)

        menu = self.menuBar()
        toolsMenu = menu.addMenu("Tools")

        #adds the button for the spell check
        spellCheck = QAction("Spell Check", self)
        spellCheck.triggered.connect(self.spellCheck)
        toolsMenu.addAction(spellCheck)

        self.show()

    #spell cheker that highlights misspelled words
    def spellCheck(self):
        #converts everything in the text file to plain text
        text = self.text_box.toPlainText()
        words = text.split()

        #puts a cursor over the text file that goes down each word
        cursor = self.text_box.textCursor()
        cursor.movePosition(1, 0, 1)
        cursor.movePosition(14, 1, 1)

        #red highlight format for mispelled words
        highlight = QTextCharFormat()
        highlight.setBackground(QtGui.QColor("#FFCCCB"))

        #goes through every word in the text file
        for i in words:
            #if the word is found in a dictionary or has an uppercase, number, or symbol character found is set to 1
            found = 0

            #checks for capital letters
            for char in i:
                if char.isupper():
                    found = 1

            #checks for numbers or symbols
            if not i.isalnum() or i.isdigit():
                found = 1

            #goes through the dictionary file to check if the current word is present
            if found == 0:
                with open("dictionaries/dictionary.txt", "r") as file1:
                    for j in file1:
                        j = j.strip()
                        if i == j:
                            found = 1
                            break

            #goes through the custom dictionary file to check if the current word is present
            if found == 0:
                with open("dictionaries/custom_dictionary.txt", "r") as file2:
                    for k in file2:
                        if i == k:
                            found = 1
                            break

            #if the word was not found, mark it
            if found == 0:
                #set the word within the text file to be highlighted
                cursor.setCharFormat(highlight)

            #moves to the next word within the text file
            cursor.movePosition(18, 0, 1)
            cursor.movePosition(5, 0, 1)
            cursor.movePosition(14, 1, 1)

if __name__ == '__main__':
    app = QApplication(sys.argv)

    window = Window()

    window.show()

    sys.exit(app.exec())


'''
    Power Macintosh ROM disassembler.

    Author: Max Poliakovski 2020-2021

    Usage:
    python3 PowerRomDasm.py --rom_path=[path to a Power Macintosh ROM dump]
'''
from argparse import ArgumentParser
from ruamel.yaml import YAML

from capstone import *
from capstone.m68k import *

import os
import struct

def bit_not(n, numbits=32):
    return (1 << numbits) - 1 - n

def align(n, m):
    return (n + m - 1) & bit_not(m - 1)

''' Capstone-based disassembler for 68k code.'''
class M68KDasm:
    def __init__(self, cb):
        self.rom_cb = cb
        self.cse = Cs(CS_ARCH_M68K, CS_MODE_M68K_040)
        self.cse.detail = True
        self.labels = {}

    def dasm_single(self, address, code):
        ''' Disassble single 68k instruction with the Capstone engine. '''

        # detect A-Traps and disassemble them ourselves
        if (code[0] & 0xF0) == 0xA0:
            from mactraps import TRAP_TABLE

            trap_num = (code[0] << 8) | code[1]
            if trap_num in TRAP_TABLE:
                return ((TRAP_TABLE[trap_num], [], 2))
            else:
                return (('dc.w', [hex(trap_num)], 2))

        # disassemble non-trap instructions with Capstone
        instrs = self.cse.disasm(code, address)
        return next(instrs)

    def dasm_region(self, addr, size, data):
        pp_dasm = []
        last_addr = addr + size
        offset = 0

        while addr < last_addr:
            # prefetch binary data (2 >= bytes <= 10) for the next instruction
            bin_length = min(last_addr - addr, 10)
            bin_prefetch = bytearray()
            for i in range(bin_length):
                bin_prefetch.append(data[offset+i])

            instr = self.dasm_single(addr, bin_prefetch)

            if not isinstance(instr, CsInsn):
                pp_dasm.append({'addr': addr, 'mnem': instr[0], 'ops': instr[1]})
                addr += instr[2]
                offset += instr[2]
                continue

            op_list = instr.op_str.split(',')
            #print(op_list)
            ops = []
            for op in instr.operands:
                #print(op.type)
                if op.type == M68K_OP_MEM:
                    #print(op.address_mode)
                    if op.address_mode == M68K_AM_PCI_DISP:
                        ea = addr + op.mem.disp + 2
                        flag,sym = self.rom_cb.get_symbol(ea)
                        if flag:
                            ops.append(sym)
                        else:
                            label = 'l_{:x}'.format(ea)
                            ops.append(label)
                            if ea not in self.labels:
                                self.labels[ea] = label
                        # discard current op because we've just replaced it
                        op_list.pop(0)
                    elif op.address_mode == M68K_AM_PCI_INDEX_BASE_DISP:
                        ops.append(instr.op_str)
                    else:
                        ops.append(op_list.pop(0))
                elif op.type == M68K_OP_BR_DISP:
                    if op.address_mode == M68K_AM_BRANCH_DISPLACEMENT:
                        ea = addr + op.br_disp.disp + 2
                        flag,sym = self.rom_cb.get_symbol(ea)
                        if flag:
                            ops.append(sym)
                        else:
                            label = 'l_{:x}'.format(ea)
                            ops.append(label)
                            if ea not in self.labels:
                                self.labels[ea] = label
                        # discard current op because we've just replaced it
                        op_list.pop(0)
                    else:
                        ops.append(op_list.pop(0))
                else:
                    ops.append(op_list.pop(0))
            pp_dasm.append({'addr': addr, 'mnem': instr.mnemonic, 'ops': ops})
            addr += instr.size
            offset += instr.size
            #print(ops)
            #print(self.labels)
        for instr in pp_dasm:
            if instr['addr'] in self.labels:
                print('\n' + self.labels[instr['addr']] + ':')
            print(hex(instr['addr']).ljust(15), end='')
            print(instr['mnem'], '\t', end='')
            print(','.join(instr['ops']))

class ROMDisassembler:
    def __init__(self, rom_data, rom_db):
        self.rom_data = rom_data
        self.rom_db = rom_db
        self.start_addr = rom_db['main_info']['phys_addr']
        self.m68k_dasm = M68KDasm(self)

    def get_symbol(self, addr):
        offset = addr - self.start_addr
        if offset in self.rom_db['annot_items']:
            return (True, self.rom_db['annot_items'][offset]['label'])
        else:
            return (False, '')

    def fmt_single_entry(self, format, size, offset):
        print(hex(self.start_addr + offset).ljust(15), end='')
        if format == 'hex':
            if size == 1:
                print("dc.b\t0x%X" % struct.unpack('>B', self.rom_data[offset:offset+1]))
            elif size == 2:
                print("dc.w\t0x%X" % struct.unpack('>H', self.rom_data[offset:offset+2]))
            elif size == 4:
                print("dc.l\t0x%X" % struct.unpack('>I', self.rom_data[offset:offset+4]))
            else:
                print("INVALID SIZE!")
        elif format == 'dec':
            if size == 1:
                print("dc.b\t%d" % struct.unpack('>B', self.rom_data[offset:offset+1]))
            elif size == 2:
                print("dc.w\t%d" % struct.unpack('>H', self.rom_data[offset:offset+2]))
            elif size == 4:
                print("dc.l\t%d" % struct.unpack('>I', self.rom_data[offset:offset+4]))
            else:
                print("INVALID SIZE!")
        elif format == 'offset':
            dest_offset = struct.unpack('>I', self.rom_data[offset:offset+4])[0]
            if dest_offset in self.rom_db['annot_items']:
                symbol = self.rom_db['annot_items'][dest_offset]['label']
                print("dc.l\t" + symbol + '-BaseOfRom')
            else:
                print("dc.l\t0x%X" % dest_offset)

    def dasm_regions(self, start_addr, size, data, regions):
        self.labels = {}
        for reg in regions:
            if reg[2] == 'align':
                print(hex(start_addr + reg[0]).ljust(15), end='')
                print('align\t' + str(reg[3]))
            elif reg[2] == 'code':
                reg_size = reg[1] - reg[0] + 1
                self.m68k_dasm.dasm_region(start_addr + reg[0], reg_size,
                    data[reg[3]:reg[3]+reg_size])
            elif reg[2] == 'int':
                print("")
                print((reg[1] + ':').ljust(15))
                self.fmt_single_entry(reg[3], reg[4], reg[0])
            else:
                print("Unknown region type " + reg[2])

    def parse_subregs(self, start, size, subregs):
        #print("This entry has subregions", subregs)
        regs = []
        reg_start = start
        for reg in subregs:
            if reg['type'] == 'align':
                offset = reg['offset']
                if offset < reg_start or offset >= (start + size):
                    print("Invalid subregion offset: 0x%X" % offset)
                    return regs
                regs.append((reg_start, offset - 1, 'code', reg_start - start))
                boundary = reg['boundary']
                reg_end = align(offset, boundary)
                regs.append((offset, reg_end - 1, 'align', boundary))
                reg_start = reg_end
                print("reg_start=%d" % reg_start)
            elif reg['type'] == 'int':
                offset = reg['offset']
                if offset < reg_start or offset >= (start + size):
                    print("Invalid subregion offset: 0x%X" % offset)
                    return regs
                regs.append((reg_start, offset - 1, 'code', reg_start - start))
                reg_size = reg['size']
                if 'label' in reg:
                    label = reg['label']
                else:
                    label = 'l_{:x}'.format(self.start_addr + offset)
                self.m68k_dasm.labels[self.start_addr + offset] = label
                regs.append((offset, label, 'int', reg['format'], reg_size))
                reg_start = offset + reg_size
            else:
                print("Unknown subregion type " + reg['type'])
                return regs
        if reg_start < (start + size):
            regs.append((reg_start, (start + size) - 1, 'code', reg_start - start))
        #print(regs)
        return regs

    def fmt_array(self, entry, offset):
        count = entry['size'] // entry['elsize']
        for i in range(count):
            self.fmt_single_entry(entry['format'], entry['elsize'], offset)
            offset += entry['elsize']

    def parse_struct(self, fields, offset):
        size_acc = 0
        for field in fields:
            if field['type'] == 'int':
                self.fmt_single_entry(field['format'], field['size'], offset)
            elif field['type'] == 'array':
                self.fmt_array(field, offset)
            else:
                print("Unknown struct field type %s" % field['type'])
            offset += field['size']
            size_acc += field['size']
        return size_acc

    def fmt_entry(self, entry, offset):
        print("")

        if entry['type'] == 'align':
            start = offset
            end   = align(start, entry['boundary'])
            print(hex(self.start_addr + start).ljust(15), end='')
            print('align\t' + str(entry['boundary']))
            return end - start

        print((entry['label'] + ':').ljust(15))

        if entry['type'] == 'array':
            self.fmt_array(entry, offset)
        elif entry['type'] == 'int':
            self.fmt_single_entry(entry['format'], entry['size'], offset)
        elif entry['type'] == 'code':
            size = entry['size']
            if entry['arch'] == '68k':
                if 'subregs' in entry:
                    regs = self.parse_subregs(offset, size, entry['subregs'])
                else:
                    regs = [(offset, offset + size - 1, 'code', 0)]
                self.dasm_regions(self.start_addr, size, self.rom_data[offset:offset+size], regs)
            elif entry['arch'] == 'ppc':
                print("PPC disassembler not implemented yet")
            else:
                print("Unknown code region architecture " + entry['arch'])
        elif entry['type'] == 'fixlenstr': # fixed-length string
            print(hex(self.start_addr + offset).ljust(15), end='')
            str_len = entry['size']
            fmt_str = '%is' % str_len
            print('"%s"' % struct.unpack(fmt_str, self.rom_data[offset:offset+str_len])[0].decode('mac_roman'))
        elif entry['type'] == 'struct':
            return self.parse_struct(entry['fields'], offset)

        return entry['size']

    def dasm_region(self, start, end):
        offset = start
        while offset < end:
            if offset in self.rom_db['annot_items']:
                entry = self.rom_db['annot_items'][offset]
                size = self.fmt_entry(entry, offset)
                offset += size
            else:
                print(hex(self.start_addr + offset).ljust(15), end='')
                print("dc.b\t0x%X" % struct.unpack('>B', self.rom_data[offset:offset+1]))
                offset += 1

if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument('--rom_path', type=str,
                        dest='rom_path',
                        help='path to a PowerMacintosh ROM file to process',
                        metavar='ROM_PATH', required=True)
    parser.add_argument('--start', type=lambda x: int(x,0),
                        dest='start_offs', default=0,
                        help='offset to the start of the region to disassemble',
                        required=False,
                        )
    parser.add_argument('--end', type=lambda x: int(x,0),
                        dest='end_offs', default=0x500,
                        help='offset to the end of the region to disassemble',
                        required=False,
                        )

    opts = parser.parse_args()

    with open(opts.rom_path, 'rb') as rom_file:
        rom_file.seek(0, 2)
        rom_size = rom_file.tell()
        if rom_size != (4 * 1024 * 1024):
            print("Invalid ROM file size %d (expected 4 MB)" % rom_size)

        # just load the whole ROM image into memory
        rom_file.seek(0, 0)
        rom_data = rom_file.read()

        check_sum = struct.unpack('>I', rom_data[0:4])[0]
        print("ROM Checksum: %X" % check_sum)

        my_path = os.path.dirname(os.path.realpath(__file__))

        db_name = 'ROMDB_' + '{:x}'.format(int(check_sum)).upper() + '.yaml'

        with open(my_path + '/database/' + db_name, 'rb') as db_file:
            yaml = YAML()
            annot_db = yaml.load(db_file)

            print(annot_db['main_info']['name'])

            rdasm = ROMDisassembler(rom_data, annot_db)
            rdasm.dasm_region(opts.start_offs, opts.end_offs)


"""
This contains utility functions and definitions used in calc_tilts_prec_avg_regularized.py and
hybrid_spin_evolution.py

N. K. Johnson-McDaniel, 2021
"""

import numpy as np

from lal import G_SI, C_SI, MSUN_SI

import warnings
from warnings import warn

# Define the kg to m and s conversions

kg_to_m = G_SI/C_SI**2

kg_to_s = G_SI/C_SI**3

# Setup for error handling

class Error(Exception):
    """Base class for exceptions in this module"""
    pass

class NonprecessingError(Error):
    """Exception raised when the evolution finds that the system is nonprecessing to numerical accuracy."""

    def __init__(self, message):
        self.message = message

# Setup for warnings

class Warn(UserWarning):
    """Base class for warnings in this module"""
    pass

class ValueWarning(Warn):
    """Warning raised when an input is dubious"""
    pass

# Make warnings cleaner

def warning_formatter(message, category, filename, lineno, file=None, line=None):
    return '%s: %s\n'%(category.__name__, message)

warnings.formatwarning = warning_formatter

# Define functions

def format_error(err):
        return "%s: %s"%(type(err).__name__, err)

def check_masses(m1, m2):
    if m1 <= 0. or m2 <= 0.:
        raise ValueError(
            "The input masses must both be positive, while they are m1, m2 = %e, %e kg" % (m1, m2))

    if m1 < 0.09*MSUN_SI or m2 < 0.09*MSUN_SI:
        warn("One or both of the masses is rather small: m1 = %e kg = %e Msun, m2 = %e kg = %e Msun. "
             "The masses must be input in kg." % (m1, m1/MSUN_SI, m2, m2/MSUN_SI), ValueWarning)

def eq_mass_check(m1, m2, Lf):
    if m1 == m2:
        if Lf is None:
            raise ValueError(
                "Cannot compute tilts at infinity for exactly equal-mass binaries, as those quantities are not well "
                "defined in this case.")
        else:
            raise ValueError(
                "The computation of the bounds and average value of the tilts is not yet implemented for exactly "
                "equal-mass binaries.")

def check_spin_mags(chi1, chi2):
    if chi1 < 0. or chi1 > 1. or chi2 < 0. or chi2 > 1.:
        raise ValueError(
            "The magnitudes of the spins must both be between 0 and 1, while they are chi1, chi2 = %f, %f" % (chi1, chi2))

def check_tilts(tilt1, tilt2):
    if tilt1 < 0. or tilt1 > np.pi or tilt2 < 0. or tilt2 > np.pi:
        raise ValueError(
            "The tilt angles must both be between 0 and pi, while they are tilt1, tilt2 = %f, %f" % (tilt1, tilt2))

def check_fref(fref, m1, m2, evol_type):
    if fref <= 0.:
        raise ValueError(
            "The reference frequency must be positive, while it is fref = %f Hz" % fref)

    f_ISCO = 6.**(-1.5)/(np.pi*(m1 + m2)*kg_to_s)

    if fref > f_ISCO:
        warn("The reference frequency should not be close to merger, where the %s evolution is highly inaccurate, "
             "while it is fref = %f Hz, which is greater than the Schwarzschild ISCO frequency associated "
             "with the binary's total mass of %f Hz"%(evol_type, fref, f_ISCO), ValueWarning)

def package_tilts(tilt1, tilt2, Lf, swap):
    """
    Package tilts to be returned by prec_avg_tilt_comp_vec_inputs() or prec_avg_tilt_comp() depending on whether
    Lf is None or not. Also swaps the tilts if necessary. Only works in the case when Lf is not None when the
    min, max, and average values are all the same.

    Inputs:

    tilt1, tilt2: Tilts
    Lf: Final orbital angular momentum (here just acts as a switch depending on whether it is None or not)
    swap: Whether to swap tilt1 and tilt2 before returning (True) or not (False)

    Output: dictionary with entries 'tilt1_inf', 'tilt2_inf' for evolution to infinity--Lf is None--and entries
            'tilt1_sep_min', 'tilt1_sep_max', 'tilt1_sep_avg', 'tilt2_sep_min', 'tilt2_sep_max', 'tilt2_sep_avg'
            for evolution to a finite separation (i.e., a finite orbital angular momentum), when Lf is not None
    """

    if swap:
        tilt1, tilt2 = tilt2, tilt1

    if Lf is None:
        return {'tilt1_inf': tilt1, 'tilt2_inf': tilt2}
    else:
        return {'tilt1_sep_min': tilt1, 'tilt1_sep_max': tilt1, 'tilt1_sep_avg': tilt1, 'tilt2_sep_min': tilt2,
                'tilt2_sep_max': tilt2, 'tilt2_sep_avg': tilt2}

def evolution_error_handling(failure_mode, failure_message, failure_output, failure_output_string, Lf, swap, hybrid_evol=False):
    """
    Take care of the error message or warning and returning something for the tilts when the
    precession-averaged evolution fails. Append entries for tilts at transition in the case where hybrid evolution fails.

    Inputs:
    failure_mode: The failure mode (either "Error", "NAN", or "None")
    failure_message: The message to print in the error or warning
    failure output: What to output for the tilts in the event of a failure
    failure_output_string: The string associated with the failure output
    Lf: Final orbital angular momentum (here just acts as a switch depending on whether it is None or not)
    swap: Whether to swap tilt1 and tilt2 before returning (True) or not (False)
    hybrid_evol: Flag to invoke error handling in the hybrid evolution code. Default: False

    Output: dictionary with entries 'tilt1_inf', 'tilt2_inf' for evolution to infinity--Lf is None--and entries
            'tilt1_sep_min', 'tilt1_sep_max', 'tilt1_sep_avg', 'tilt2_sep_min', 'tilt2_sep_max', 'tilt2_sep_avg'
            for evolution to a finite separation (i.e., a finite orbital angular momentum), when Lf is not None.
            The entries of the dictionary are failure_output
            If hybrid_evol is set to True, entries 'tilt1_transition', 'tilt2_transition', 'phi12_transition', and 'f_transition' set as failure_output are appended to match the output of the hybrid evolution code.
    """

    if failure_mode == 'Error':
        raise RuntimeError(failure_message)
    else:
        failure_message += " Returning %s for the tilts."%failure_output_string

        warn(failure_message, RuntimeWarning)

        if hybrid_evol:
            hybrid_output_tilts = package_tilts(failure_output, failure_output, Lf, swap)
            hybrid_output_tilts["tilt1_transition"] = failure_output
            hybrid_output_tilts["tilt2_transition"] = failure_output
            hybrid_output_tilts["phi12_transition"] = failure_output
            hybrid_output_tilts["f_transition"] = failure_output
            return hybrid_output_tilts

        return package_tilts(failure_output, failure_output, Lf, swap)


import datetime
import pandas as pd

def store_menu_arrange(store_menu_df: pd.DataFrame, update_date):

    update = datetime.datetime.strptime(update_date, "%Y-%m-%d %H:%M:%S")
    base_time_list = store_menu_df[store_menu_df["tabGroup"] == 1]["displayEndAt"].tolist()
    base_code = store_menu_df[store_menu_df["tabGroup"] == 1]["code"].tolist()

    real_base_time = []
    valid_code_list = []
    used_code_list = []

    # 기간에 맞지 않는 추천 상품 삭제
    for idx, base_time_list in enumerate(base_time_list):
        real_base_time.append(datetime.datetime.strptime(base_time_list, "%Y-%m-%d %H:%M:%S"))
        if real_base_time[idx] <= update:
            valid_code_list.append(base_code[idx])
            store_menu_df = store_menu_df[store_menu_df["code"] != base_code[idx]]
        else:
            used_code_list.append(base_code[idx])

    return store_menu_df, valid_code_list, used_code_list

def store_home_arrange(store_home_df: pd.DataFrame, update_date):

    update = datetime.datetime.strptime(update_date, "%Y-%m-%d %H:%M:%S")
    store_home_df = store_home_df.iloc[2:]
    base_time_list = store_home_df["displayEndAt"].tolist()
    real_base_time = []

    for idx, base_time in enumerate(base_time_list):
        real_base_time.append(datetime.datetime.strptime(base_time, "%Y-%m-%d %H:%M:%S"))
        if real_base_time[idx] <= update:
            store_home_df = store_home_df[store_home_df["displayEndAt"] != base_time]

    return store_home_df

def event_management_arrange(event_management_df: pd.DataFrame, update_date, bg_list_size):
    update = datetime.datetime.strptime(update_date, "%Y-%m-%d %H:%M:%S")
    base_time_list = event_management_df["eventEndAt"].tolist()
    idx_order_index_target = []
    for idx, base_time in enumerate(base_time_list):
        if idx < 2 or not isinstance(base_time, str):
            continue
        if datetime.datetime.strptime(base_time, "%Y-%m-%d %H:%M:%S") > update:
            idx_order_index_target.append((idx, event_management_df.loc[idx, "orderIndex"]))
    idx_order_index_target.sort(key=lambda x: x[1])
    for order, target in enumerate(idx_order_index_target):
        event_management_df.loc[target[0], "orderIndex"] = order + bg_list_size + 1

    return event_management_df


"""Evaluate a trained model"""

import argparse
import logging
import os
import nibabel as nib
import numpy as np
import json
import subprocess
from glob import glob
# set tensorflow logging to FATAL before importing things that contain tensorflow
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'  # 0 = INFO, 1 = WARN, 2 = ERROR, 3 = FATAL
logging.getLogger('tensorflow').setLevel(logging.ERROR)
from predict import predict
from utilities.eval_metrics import metric_picker
from utilities.input_fn import get_study_dirs, train_test_split
from utilities.utils import Params, set_logger
from utilities.input_fn_util import normalize

# globals
EPSILON = 1e-10  # small number to avoid divide by zero errors

# define funiction to predict and evaluate one directory
def eval_pred(params, eval_dirs, pred_niis, out_dir, mask, metrics, verbose=False, redo=False, suffix=None):
    # get sorted lists of true niis, predicted niis, and masks
    true_niis = sorted([glob(eval_d + '/*' + params.label_prefix[0] + '.nii.gz')[0] for eval_d in eval_dirs])
    pred_niis = sorted(pred_niis)
    if mask:
        mask_niis = sorted([glob(eval_d + '/*' + mask + '.nii.gz')[0] for eval_d in eval_dirs])
    else:
        mask_niis = []

    # add crop to mask to allow for smaller brain masks to be used
    # ExtractRegionFromImageByMask
    # Extract a sub-region from image using the bounding box from a label image, with optional padding radius.
    # Usage : ExtractRegionFromImageByMask ImageDimension inputImage outputImage labelMaskImage [label=1] [padRadius=0]
    if mask:
        # report
        logging.info("Eval mask specified - cropping files to mask")
        # temporary output vars
        tmp_true = []
        tmp_pred = []
        tmp_mask = []
        # loop through all eval niis
        idx = 1
        for true_nii, pred_nii, mask_nii in zip(true_niis, pred_niis, mask_niis):
            logging.info("Cropping images to eval mask {} of {}...".format(idx, len(pred_niis)))
            # defined cropped outnames
            crop_true_nii = os.path.join(out_dir,
                                         os.path.basename(true_nii).split('.nii.gz')[0] + '_' + mask + '.nii.gz')
            crop_pred_nii = os.path.join(out_dir,
                                         os.path.basename(pred_nii).split('.nii.gz')[0] + '_' + mask + '.nii.gz')
            crop_mask_nii = os.path.join(out_dir,
                                         os.path.basename(mask_nii).split('.nii.gz')[0] + '_crop.nii.gz')
            # crop if not already done
            # true nii
            if not os.path.isfile(crop_true_nii) or redo:
                cmd = "ExtractRegionFromImageByMask 3 {} {} {} 1 0".format(true_nii, crop_true_nii, mask_nii)
                if verbose:
                    logging.debug(cmd)
                subprocess.call(cmd, shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
            else:
                logging.info(
                    "Cropped true image already exists at {} and will not be overwritten".format(crop_true_nii))
            # pred nii
            if not os.path.isfile(crop_pred_nii) or redo:
                cmd = "ExtractRegionFromImageByMask 3 {} {} {} 1 0".format(pred_nii, crop_pred_nii, mask_nii)
                if verbose:
                    logging.debug(cmd)
                subprocess.call(cmd, shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
            else:
                logging.info(
                    "Cropped predicted image already exists at {} and will not be overwritten".format(crop_pred_nii))
            # mask nii
            if not os.path.isfile(crop_mask_nii) or redo:
                cmd = "ExtractRegionFromImageByMask 3 {} {} {} 1 0".format(mask_nii, crop_mask_nii, mask_nii)
                if verbose:
                    logging.debug(cmd)
                subprocess.call(cmd, shell=True, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
            else:
                logging.info("Cropped mask already exists at {} and will not be overwritten".format(crop_mask_nii))
            # add cropped niis to output lists
            tmp_true.append(str(crop_true_nii))
            tmp_pred.append(str(crop_pred_nii))
            tmp_mask.append(str(crop_mask_nii))
            # increment index
            idx += 1
        # at end of loop, replace nii lists with new cropped lists
        true_niis = tmp_true
        pred_niis = tmp_pred
        mask_niis = tmp_mask

    # set up metrics variables for collecting results
    metrics_dict = {}
    # loop through each pair of niis for comparissons
    for ind, pred_nii in enumerate(pred_niis):
        logging.info("Evaluating directory {} of {}...".format(ind+1, len(pred_niis)))

        # if normalization is specified in params, then normalize the true nii and save result to eval directory
        if params.norm_labels:
            # load true image
            true_nii = nib.load(true_niis[ind])
            true_im = true_nii.get_fdata()
            # load mask
            if mask:
                mask_im = nib.load(mask_niis[ind]).get_fdata()
                true_im = true_im * (mask_im > 0.)
            # do normalization
            true_im = normalize(true_im, mode=params.norm_mode)
            # save results in eval directory and update true_im name
            new_true_nii = nib.Nifti1Image(true_im, true_nii.affine, true_nii.header)
            true_nii_out = os.path.join(out_dir, os.path.basename(true_niis[ind]).split('.nii.gz')[0] + 'n.nii.gz')
            nib.save(new_true_nii, true_nii_out)
            true_nii = true_nii_out
        else:
            true_nii = true_niis[ind]

        # handle optional mask argument
        if mask:
            mask_nii = mask_niis[ind]
        else:
            mask_nii = None

        # get metric values
        tmp = {pred_nii: {}}
        for metric in metrics:
            # calculate metric - if calculation fails return nan
            try:
                metric_val = metric_picker(metric, true_nii, pred_nii, mask_nii, mask=mask, verbose=verbose)
            except:
                logging.warning("Unable to calculate metric {} for predicted image {}".format(metric, pred_nii))
                metric_val = float("nan")
            tmp[pred_nii].update({metric: metric_val})

        # update metrics dict
        metrics_dict.update(tmp)

    # get metric averages
    metrics_dict.update({'Averages': {}})
    for metric in metrics:
        metric_avg = np.nanmean([metrics_dict[item][metric] for item in metrics_dict.keys() if not item == 'Averages'])
        metrics_dict['Averages'].update({metric: metric_avg})

    # save metrics
    # use prefix string to ID which mask was used for evaluation
    if mask:
        mask_str = mask
    else:
        mask_str = 'no_mask'
    # handle extra suffix option
    if suffix:
        metrics_filepath = os.path.join(out_dir, "{}_{}_eval_metrics.json".format(mask_str, suffix))
    else:
        metrics_filepath = os.path.join(out_dir, "{}_eval_metrics.json".format(mask_str))
    # write output json
    with open(metrics_filepath, 'w+', encoding='utf-8') as fi:
        json.dump(metrics_dict, fi, ensure_ascii=False, indent=4)

    return metrics_dict

# executed  as script
if __name__ == '__main__':

    # parse input arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--param_file', default=None,
                        help="Path to params.json")
    parser.add_argument('-o', '--out_dir', default=None,
                        help="Optionally specify output directory")
    parser.add_argument('-m', '--mask', default=None,
                        help="Whole brain mask for masking out predictions background")
    parser.add_argument('-e', '--eval_mask', default=None,
                        help="Optionally specify a mask nii prefix for evaluation. All values > 0 are included in mask")
    parser.add_argument('-v', '--verbose', default=False, action="store_true",
                        help="Verbose terminal output flag")
    parser.add_argument('-t', '--metrics', default=['cc', 'mi', 'ssim', 'mse', 'nrmse', 'smape', 'logac', 'medsymac'],
                        nargs='+', help="Metric or metrics to be evaluated (can specify multiple)")
    parser.add_argument('-c', '--checkpoint', default='best',
                        help="'best' or 'last' - whether to use best or last weights for inference")
    parser.add_argument('-x', '--overwrite', default=False, action="store_true",
                        help="Use this flag to overwrite existing data")
    parser.add_argument('-l', '--baseline', default=None,
                        help="Prefix for image contrast to use in place of predictions")
    parser.add_argument('-r', '--rename', default=None,
                        help="Optionally rename the base folder for study dirs (useful when trained in production env)")
    parser.add_argument('-n', '--rename_label', default=None,
                        help="Optionally rename the label prefix in param file (useful when trained in production env)")
    parser.add_argument('-f', '--force_cpu', default=False,
                        help="Disable GPU and force all computation to be done on CPU",
                        action='store_true')

    # handle param argument
    args = parser.parse_args()
    assert args.param_file, "Must specify param file using --param_file"
    assert os.path.isfile(args.param_file), "No json configuration file found at {}".format(args.param_file)
    my_params = Params(args.param_file)
    # turn of distributed strategy and mixed precision
    my_params.dist_strat = None
    my_params.mixed_precision = False

    # handle label replacement
    if args.rename_label:
        my_params.label_prefix = [args.rename_label]

    # determine model dir
    if my_params.model_dir == 'same':  # this allows the model dir to be inferred from params.json file path
        my_params.model_dir = os.path.dirname(args.param_file)
    if not os.path.isdir(my_params.model_dir):
        raise ValueError("Specified model directory does not exist")

    # handle out_dir argument
    if args.out_dir:
        assert os.path.isdir(args.out_dir), "Specified output directory does not exist: {}".format(args.out_dir)
    else:
        args.out_dir = os.path.join(os.path.dirname(args.param_file), 'evaluation')
        if not os.path.isdir(args.out_dir):
            os.mkdir(args.out_dir)

    # set up logger, delete old log file if overwrite param is set to yes
    log_path = os.path.join(args.out_dir, 'evaluate.log')
    if os.path.isfile(log_path) and my_params.overwrite == 'yes':
        os.remove(log_path)
    set_logger(log_path)
    logging.info("Log file created at " + log_path)

    # get list of study valid study directories - optionally change base directory name
    study_dirs = get_study_dirs(my_params, change_basedir=args.rename, mode='eval')

    # separate eval dirs from list of all study dirs using train fraction (same function used by train.py)
    _, my_eval_dirs = train_test_split(study_dirs, my_params)

    # handle force cpu argument
    if args.force_cpu:
        logging.info("Forcing CPU (GPU disabled)")
        os.environ["CUDA_VISIBLE_DEVICES"] = "-1"

    # predict output niis
    niis_pred = predict(my_params, my_eval_dirs, args.out_dir, mask=args.mask, checkpoint=args.checkpoint)

    # handle CSV out
    if niis_pred[0].endswith('.csv'):
        logging.info("Evaluation output CSV created at {}".format(niis_pred[0]))
        exit()

    # handle baseline predictions
    if args.baseline:
        niis_pred = [glob("{}/*{}.nii.gz".format(eval_dir, args.baseline))[0] for eval_dir in my_eval_dirs if
                     os.path.isfile(glob("{}/*{}.nii.gz".format(eval_dir, args.baseline))[0])]

    # evaluate
    my_metrics_dict = eval_pred(my_params, my_eval_dirs, niis_pred, args.out_dir, args.eval_mask, args.metrics,
                                verbose=args.verbose, redo=args.overwrite, suffix=args.baseline)

    # report
    for my_metric in my_metrics_dict['Averages'].keys():
        logging.info("Average {} = {}".format(my_metric, my_metrics_dict['Averages'][my_metric]))


from flask import Flask, render_template, request,jsonify
import nltk
import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
import re
import nltk
stemmer = nltk.SnowballStemmer("english")
from nltk.corpus import stopwords
import string
stopword = set(stopwords.words('english'))
import joblib
from sklearn.metrics import classification_report # Import jsonify

app = Flask(__name__, static_url_path='/static')

# Load data and preprocess it
try:
    nltk.data.find('corpora/stopwords.zip')
    print("Lagi dicari stopwordsnya")
except LookupError:
    nltk.download('stopwords')
    print("Download bruh")

data = pd.read_csv("labeled_data.csv")

data["labels"] = data["class"].map({0: "Hate Speech Detected", 1: "Offensive Language", 2: "No Hate and Offensive"})

data = data[["tweet", "labels"]]

def clean(text):
    text = str(text).lower()
    text = re.sub('\[.*?\]', '', text)
    text = re.sub('https?://\S+|www\.\S+', '', text)
    text = re.sub('<.*?>+', '', text)
    text = re.sub('[%s]' % re.escape(string.punctuation), '', text)
    text = re.sub('\n', '', text)
    text = re.sub('\w*\d\w*', '', text)
    text = [word for word in text.split(' ') if word not in stopword]
    text = " ".join(text)
    text = [stemmer.stem(word) for word in text.split(' ')]
    text = " ".join(text)
    return text

data["tweet"] = data["tweet"].apply(clean)

x = np.array(data["tweet"])
y = np.array(data["labels"])

tfidf_vectorizer = TfidfVectorizer(max_features=5000)
X = tfidf_vectorizer.fit_transform(x)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

# Muat model dari file jika ada, jika tidak latih model baru
try:
    clf = joblib.load('decision_tree_model.pkl')
    print("Model loaded from file.")
except FileNotFoundError:
    print("Model not found. Training new model...")

    param_grid = {'criterion': ['gini', 'entropy'],
                  'max_depth': [None, 10, 20, 30],
                  'min_samples_split': [2, 5, 10]}

    clf = GridSearchCV(DecisionTreeClassifier(), param_grid, cv=5)
    clf.fit(X_train, y_train)

    # Simpan model ke file
    joblib.dump(clf, 'decision_tree_model.pkl')
    print("New model trained and saved to file.")

# Evaluasi model
y_pred = clf.predict(X_test)
print("Classification Report:\n", classification_report(y_test, y_pred))

offensive_hate_words = ["fucking", "kontol", "you such a dick head", "so dumb"]

# Define route for the web interface
@app.route('/', methods=['GET', 'POST'])

def index():
    if request.method == 'POST':
        user_input = request.form['user_input']
        cleaned_input = clean(user_input)
        if any(word in cleaned_input for word in offensive_hate_words):
            automatic_label = "Hate Speech Detected"
        else:
            data = tfidf_vectorizer.transform([cleaned_input]).toarray()
            a = clf.predict(data)
            automatic_label = a[0]
        return render_template('/index.html', automatic_label=automatic_label, user_input=user_input)
    return render_template('/index.html', automatic_label='', user_input='')

@app.route('/api', methods=['POST'])  # Only handle POST requests
def api():
    if request.method == 'POST':
        user_input = request.form.get('user_input')  # Get user_input from the form data

        # Process the input and classify
        cleaned_input = clean(user_input)
        if any(word in cleaned_input for word in offensive_hate_words):
            automatic_label = "Hate Speech Detected"
        else:
            data = tfidf_vectorizer.transform([cleaned_input]).toarray()
            a = clf.predict(data)
            automatic_label = a[0]

        # Return JSON response
        response = {
            "user_input": user_input,
            "automatic_label": automatic_label
        }
        return jsonify(response)  # Convert response to JSON and return
if __name__ == '__main__':
    app.run(debug=True)


from functools import reduce

pessoas = [
    {'nome': 'Pedro', 'idade': 11},
    {'nome': 'Mariana', 'idade': 18},
    {'nome': 'Arthur', 'idade': 26},
    {'nome': 'Rebeca', 'idade': 6},
    {'nome': 'Tiago', 'idade': 19},
    {'nome': 'Gabriela', 'idade': 17},
]

menores = filter(lambda p: p['idade'] < 18, pessoas)
soma_idades_dos_menores = reduce(
    lambda idades, p: idades + p['idade'], menores, 0)
print(soma_idades_dos_menores)

soma_idades = reduce(lambda idades, p: idades + p['idade'], pessoas, 0)
print(soma_idades)

so_idades = map(lambda p: p['idade'], pessoas)
so_menores = filter(lambda idade: idade < 18, so_idades)
soma_so_das_idades = reduce(
    lambda idades, idade: idades + idade, so_menores, 0)
print(soma_so_das_idades)


import pygame
import pynput
from pynput.mouse import Button, Controller
import time

mouse = Controller()
keyboard = pynput.keyboard.Controller()

def map(val, min1, max1, min2, max2):
    range1 = max1 - min1
    range2 = max2 - min2

    scale = range2 / range1
    return (val - min1) * scale + min2

def main():
    xmovement = 0
    ymovement = 0
    sideMovement = 0
    gas = False
    brake = False

    joysticks = []

    # for all the connected joysticks
    for i in range(0, pygame.joystick.get_count()):
        # create an Joystick object in our list
        joysticks.append(pygame.joystick.Joystick(i))
        # initialize them all (-1 means loop forever)
        joysticks[-1].init()
        # prints controller name
        print( "Detected joystick ",joysticks[-1].get_name(),"")

    while True:
        for event in pygame.event.get():
                if event.type == pygame.JOYAXISMOTION:
                    print( "Joystick ",joysticks[event.joy].get_name()," axis",event.axis,"motion.", event.value)
                    if event.axis == 0:
                        # xmovement = map(event.value, -1, 1, -1680, -1)
                        xmovement = map(event.value, -1, 1, -100, 100)

                    if event.axis == 1:
                        if (event.value > 0.02):    # brake pedal pressed
                            brake = True
                            gas = False
                        elif (event.value < -0.02): # gas pedal pressed
                            gas = True
                            brake = False
                        else:
                            brake = False
                            gas = False

                elif event.type == pygame.JOYBUTTONDOWN:
                    print( "Joystick ",joysticks[event.joy]," button",event.button,"down.")
                    if event.button == 0:
                        mouse.press(Button.left)
                    elif event.button == 1:
                        mouse.press(Button.right)
                    elif event.button == 4:
                        keyboard.press("e")
                    elif event.button == 5:
                        keyboard.press("f")
                    elif event.button == 6:
                        return      # Terminate script
                    elif event.button == 7:
                        keyboard.press("x")

                elif event.type == pygame.JOYBUTTONUP:
                    print( "Joystick ",joysticks[event.joy].get_name()," button",event.button,"up.")
                    if event.button == 0:
                        mouse.release(Button.left)
                    elif event.button == 1:
                        mouse.release(Button.right)
                elif event.type == pygame.JOYHATMOTION:
                    print( "Joystick ",joysticks[event.joy].get_name()," hat switch at ",event.value," position ")

                    if event.value == (0,1):    # upward hat switch placement
                        ymovement = -5
                    elif event.value == (0,-1): # downward hat switch placement
                        ymovement = 5
                    elif event.value == (0,0):  # middle hat switch placement
                        ymovement = 0
                        sideMovement = 0
                    if event.value == (1,0):    # right hat switch placement
                        sideMovement = 1
                    elif event.value == (-1,0): # left hat switch placement
                        sideMovement = -1

        #   WASD movements ↓
        if gas:
            keyboard.press("w")
        else:
            keyboard.release("w")

        if brake:
            keyboard.press("s")
        else:
            keyboard.release("s")

        if sideMovement == 1:
            keyboard.press("d")
            keyboard.release("a")
        elif sideMovement == -1:
            keyboard.press("a")
            keyboard.release("d")
        else:
            keyboard.release("a")
            keyboard.release("d")

        mouse.move(xmovement, ymovement)    # Mouse movement

        time.sleep(0.02)                    # time delay prevents unregistered or delayed actions in apps

pygame.init()
main()
pygame.quit()


import logging
from typing import Mapping, Any, Tuple
from uuid import UUID
from weakref import ref
import pickle as pkl
from datetime import datetime, timedelta

from PyQt5.QtCore import Qt, QRectF
from PyQt5.QtGui import QIcon, QGradient, QImage, QPixmap, QPainter, QPen, QBrush
from PyQt5.QtWidgets import (QGraphicsItem, QGraphicsObject, QGraphicsTextItem,
                             QGraphicsPixmapItem, QStyleOptionGraphicsItem, QWidget,
                             QGraphicsSceneMouseEvent, QGraphicsSceneDragDropEvent,
                             QGraphicsSceneContextMenuEvent, QGraphicsRectItem)

from uwsift.common import Flags
from .common import (CoordTransform, GFXC, MIMETYPE_TIMELINE_COLORMAP, MIMETYPE_TIMELINE_TRACK,
                     mimed_colormap, mimed_track, recv_mime, VisualState)

LOG = logging.getLogger(__name__)

def _wtf_recursion():
    from inspect import getouterframes, currentframe
    level = len(getouterframes(currentframe()))
    LOG.debug("frame boundingRect depth {}".format(level))

def _perform_context_menu(event, scene, track, frame) -> bool:
    """Custom context menu handler shared by QTrackItem and QFrameItem
    return true if the menu fully activated and did something
    """
    qm = scene.menu_for_track(track, frame)
    if qm is not None:
        # pos: QPointF = event.pos()
        # wdgt = event.widget()
        menu, lut = qm
        if not lut or not menu:
            LOG.warning("scene menu generator returned nothing to do")
            return False
        event.accept()
        sel = menu.exec_(event.screenPos())  # wdgt.mapToGlobal(pos.toPoint()))
        todo = lut.get(sel)
        if callable(todo):
            todo(scene=scene, track=track, frame=frame)
            return True
        else:
            return False

class QTrackItem(QGraphicsObject):
    """ A group of Frames corresponding to a timeline
    This allows drag and drop of timelines to be easier
    """
    frames = None  # Iterable[QFrameItem], maintained privately between track and frame
    _scene = None  # weakref to scene
    _scale: CoordTransform = None
    _track: str = None  # tracks are family::category, e.g. IMAGE:geo:toa_reflectance:11µm::GOESR-PUG:GOES-16:ABI:CONUS
    _z: int = None  # our track number as displayed, 0 being highest on screen, with larger Z going downward
    _title: str = None
    _subtitle: str = None
    _icon: QIcon = None  # e.g. whether it's algebraic or RGB
    # arbitrary key-value store for selecting by metadata;
    # in our case this often includes item family for seleciton
    _metadata: Mapping = None
    _tooltip: str = None
    _state: Flags = None  # VisualState Flags determine how it's being presented
    _colormap: [QGradient, QImage, QPixmap] = None
    _min: float = None
    _max: float = None
    _dragging: bool = False  # whether or not a drag is in progress across this item
    # position in scene coordinates is determined by _z level and starting time of first frame, minus _left_pad
    # bounds of the track in scene coordinates, assuming 0,0 corresponds to vertical center of left edge of
    # frame representation
    _bounds: QRectF = QRectF()
    _gi_title: QGraphicsTextItem = None
    _gi_subtitle: QGraphicsTextItem = None
    _gi_icon: QGraphicsPixmapItem = None
    _gi_colormap: QGraphicsPixmapItem = None

    def __init__(self, scene, scale: CoordTransform, track: str, z: int,
                 title: str, subtitle: str = None, icon: QIcon = None, metadata: dict = None,
                 tooltip: str = None, state: Flags = None, colormap: [QGradient, QImage] = None,
                 min: float = None, max: float = None):
        """Create a track and connect it to its Scene
        """
        super(QTrackItem, self).__init__()
        self.frames = []
        self._scene = ref(scene) if scene else None
        self._scale = scale
        self._track = track
        self._z = z
        self._title = title
        self._subtitle = subtitle
        self._icon = icon
        self._metadata = metadata or {}
        self._tooltip = tooltip
        self._state = Flags(state or [])
        self._colormap = colormap
        self._min, self._max = min, max
        self.setToolTip(tooltip or "{}\n{}".format(title, subtitle))
        self.setFlag(QGraphicsItem.ItemClipsChildrenToShape, enabled=True)
        self.update_pos_bounds()
        self._update_decorations()
        scene.add_track(self)
        self.setAcceptDrops(True)

    def addFrame(self, frame):
        self._scene().add_frame(frame)
        self.frames.append(frame)

    # @property
    # def scene(self):
    #     return self._scene()
    #
    # @scene.setter
    # def scene(self, s):
    #     self._scene = ref(s)

    @property
    def track(self) -> str:
        return self._track

    @property
    def scene_(self):  # can't use just .scene due to Qt C++ scene()
        return self._scene()

    @property
    def z(self) -> int:
        return self._z

    @z.setter
    def z(self, new_z: int):
        self._z = new_z

    @property
    def default_frame_pen_brush(self):
        return self._scene().default_frame_pen_brush

    def _update_decorations(self):
        """Add decor sub-items to self
        title, subtitle, icon, colormap
        these are placed left of the local origin inside the _left_pad area
        """
        scene = self._scene()
        if self._title:
            self._gi_title = it = self._gi_title or scene.addSimpleText(self._title)
            it.setParentItem(self)
            it.setPos(GFXC.track_title_pos)
        if self._subtitle:
            self._gi_subtitle = it = self._gi_subtitle or scene.addSimpleText(self._subtitle)
            it.setParentItem(self)
            it.setPos(GFXC.track_subtitle_pos)
        # FUTURE: add draggable color-map pixmap

    # commands to cause item updates and then propagate back to the scene

    def set_colormap(self, cmap: mimed_colormap):
        """Inform scene that the user wants all tracks in our family to use this colormap
        """
        LOG.warning("set colormap from dragged colormap not yet implemented")

    def insert_track_before(self, track: mimed_track):
        """Inform scene that user wants a dragged scene moved to before us in the z-order"""
        LOG.warning("reorder tracks using dragged track not yet implemented")

    # painting and boundaries

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: QWidget = None):
        # LOG.debug("QTrackItem.paint")
        # _wtf_recursion()
        pen, brush = self._scene().default_track_pen_brush
        rect = self.boundingRect()
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setBrush(brush)
        painter.setPen(pen)

        # draw outer boundary
        painter.drawRoundedRect(rect, GFXC.track_corner_radius1, GFXC.track_corner_radius2, Qt.RelativeSize)

    def boundingRect(self) -> QRectF:
        #     if self._bounds is None:
        #         return self.update_pos_and_bounds()
        return self._bounds

    # click events / drag departures

    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        LOG.debug("QTrackItem mouse-down")
        return super(QTrackItem, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        LOG.debug("QTrackItem mouse-up")
        return super(QTrackItem, self).mouseReleaseEvent(event)

    def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent):
        # pos = self.mapToScene(event.pos())
        if not _perform_context_menu(event, self.scene_, self._track, None):
            return super(QTrackItem, self).contextMenuEvent(event)

    # handle drag and drop arrivals

    def dragEnterEvent(self, event: QGraphicsSceneDragDropEvent):
        self._dragging = True

        # test the content being dragged to see if it's compatible; if so, accept event
        mime = event.mimeData()
        if mime.hasFormat(MIMETYPE_TIMELINE_COLORMAP):
            event.setAccepted(True)
        elif mime.hasFormat(MIMETYPE_TIMELINE_TRACK):
            # FIXME: implement animated re-arrange of tracks
            event.setAccepted(True)
        else:
            event.setAccepted(False)

    def dragLeaveEvent(self, event: QGraphicsSceneDragDropEvent):
        self._dragging = False
        event.setAccepted(False)

    def dropEvent(self, event: QGraphicsSceneDragDropEvent):
        colormap = recv_mime(event, MIMETYPE_TIMELINE_COLORMAP)
        if colormap is not None:
            self.set_colormap(colormap)
            return
        new_track_before = recv_mime(event, MIMETYPE_TIMELINE_TRACK)
        if new_track_before is not None:
            self.insert_track_before(new_track_before)
        _ = pkl.loads(event.mimeData().data())
        event.setAccepted(False)

    # working with Frames as sub-items, updating and syncing position and extents

    def _iter_frame_children(self):
        return list(self.frames)
        # children = tuple(self.childItems())
        # # LOG.debug("{} children".format(len(children)))
        # for child in children:
        #     if isinstance(child, QFrameItem):
        #         yield child

    def _time_extent_of_frames(self):
        """start time and duration of the frames held by the track
        """
        s, e = None, None
        for child in self._iter_frame_children():
            # y relative to track is 0
            # calculate absolute x position in scene
            # assert (child.uuid != self.uuid)
            t, d = child.td
            s = t if (s is None) else min(t, s)
            e = (t + d) if (e is None) else max(e, t + d)
        if e is None:
            LOG.info("empty track cannot determine its horizontal extent")
            return None, None
        return s, e - s

    def update_pos_bounds(self):
        """Update position and bounds of the Track to reflect current TimelineCoordTransform, encapsulating frames owned
        Note that the local x=0.0 corresponds to the time of the first frame in the track
        This is also the center of rotation or animation "handle" (e.g. for track dragging)
        """
        # starting time and duration of the track, computed from frames owned
        t, d = self._time_extent_of_frames()
        if (t is None) or (d is None):
            LOG.debug("no frames contained, cannot adjust size or location of QTrackItem")
            return
        LOG.debug("time extent of frames in track '{}/{}' is {} +{}".format(self._title, self._subtitle, t, d))
        # scene y coordinate of upper left corner
        # convert track extent to scene coordinates using current transform
        frames_left, frames_width = self._scale.calc_pixel_x_pos(t, d)
        screen_track_center_y = self._scale.calc_track_pixel_y_center(self.z)
        # track_left, track_width = self._scale.calc_pixel_x_pos(t - GFXC.track_left_pad,
        #     d + GFXC.track_left_pad + GFXC.track_right_pad)
        # set track position, assuming we want origin coordinate of track item to be centered vertically within item
        # bounds relative to position in scene, left_pad space to left of local origin (x<0),
        # frames and right-pad at x>=0
        self.prepareGeometryChange()
        self._bounds = QRectF(-GFXC.track_left_pad, -GFXC.track_height / 2,
                              frames_width + GFXC.track_left_pad + GFXC.track_right_pad, GFXC.track_height)
        LOG.debug("new track bounds: {}".format(self._bounds))
        # origin is at the start of the first frame contained. padding extends into negative x
        LOG.debug("track centerline placed at {},{}".format(frames_left, screen_track_center_y))
        self.setPos(frames_left, screen_track_center_y)
        self._update_decorations()

    def update_frame_positions(self, *frames):
        """Update frames' origins relative to self after TimelineCoordTransform has changed scale
        """
        myx = self.pos().x()  # my x coordinate relative to scene
        frames = tuple(frames) or self._iter_frame_children()
        for frame in frames:
            # y relative to track is 0
            # calculate absolute x position in scene
            x, _ = self._scale.calc_pixel_x_pos(frame.t)
            frame.prepareGeometryChange()
            frame.setPos(x - myx, 0.0)

class QFrameItem(QGraphicsObject):
    """A Frame
    For SIFT use, this corresponds to a single Product or single composite of multiple Products (e.g. RGB composite)
    QGraphicsView representation of a data frame, with a start and end time relative to the scene.
    Essentially a frame sprite
    """
    _state: Flags = None
    _track = None  # weakref to track we belong to
    _scale: CoordTransform = None
    _uuid: UUID = None
    _start: datetime = None
    _duration: timedelta = None
    _title: str = None
    _subtitle: str = None
    _thumb: QPixmap = None
    _metadata: Mapping = None
    _bounds: QRectF = QRectF()
    # decorations
    _gi_title = None
    _gi_subtitle = None

    def __init__(self, track: QTrackItem, scale: CoordTransform, uuid: UUID,
                 start: datetime, duration: timedelta, state: Flags,
                 title: str, subtitle: str = None, thumb: QPixmap = None,
                 metadata: Mapping[str, Any] = None):
        """create a frame representation and add it to a timeline track within a scene
        Args:
            track: which timeline to add it to
            state: initial state
            start: frame start time
            duration: frame duration
            title: title of frame
            subtitle: subtitle (below title, optional)
            thumb: thumbnail image (via pillow), optional, may not be displayed if space is not available
            uuid: UUID of workspace representation
        """
        super(QFrameItem, self).__init__()
        self._track = ref(track)
        self._state = Flags(state)
        self._scale = scale
        self._start = start
        self._duration = duration
        self._title = title
        self._subtitle = subtitle
        self._thumb = thumb
        self._metadata = metadata
        self._uuid = uuid
        self.setToolTip("{}\n{}".format(title, subtitle))
        # self._pen, self._brush = track.default_frame_pen_brush
        # if pen:
        #     LOG.debug('setting pen')
        #     self.setPen(pen)
        # if brush:
        #     LOG.debug('setting brush')
        #     self.setBrush(brush)
        track.addFrame(self)  # adds to track, which in turn adds to scene
        self.setParentItem(track)
        self.update_bounds()
        track.update_pos_bounds()
        track.update_frame_positions()
        self.setFlag(QGraphicsItem.ItemClipsChildrenToShape, enabled=True)
        self._update_decorations()
        # self.setAcceptDrops(True)

    def _update_decorations(self):
        """Add decor sub-items to self
        title, subtitle, icon, colormap
        these are placed left of the local origin inside the _left_pad area
        """
        scene = self.scene_
        if self._title:
            self._gi_title = it = self._gi_title or scene.addSimpleText(self._title)
            it.setParentItem(self)
            it.setPos(GFXC.frame_title_pos)
        if self._subtitle:
            self._gi_subtitle = it = self._gi_subtitle or scene.addSimpleText(self._subtitle)
            it.setParentItem(self)
            it.setPos(GFXC.frame_subtitle_pos)
        # FUTURE: add draggable color-map pixmap

    @property
    def scene_(self):
        trk = self._track()
        return trk.scene_

    @property
    def track(self):
        trk = self._track()
        return trk.track

    @property
    def uuid(self):
        return self._uuid

    @property
    def t(self) -> datetime:
        """Start time of frame"""
        return self._start

    @property
    def d(self) -> timedelta:
        """Duration of frame"""
        return self._duration

    @property
    def td(self) -> Tuple[datetime, timedelta]:
        return self._start, self._duration

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, new_state: VisualState):
        if new_state != self._state:
            self._state = new_state

    # painting and boundaries

    @property
    def pen_brush(self) -> Tuple[QPen, QBrush]:
        """Pen and brush to use, based on VisualState
        """
        pen, brush = self._track().default_frame_pen_brush
        s = self._state
        if VisualState.READY in s:
            brush = QBrush(Qt.green, Qt.SolidPattern)
        return pen, brush

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: QWidget = None) -> None:
        # LOG.debug("QFrameItem.paint")
        pen, brush = self.pen_brush
        rect = self.boundingRect()
        painter.setBrush(brush)
        painter.setPen(pen)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.drawRoundedRect(rect, GFXC.frame_corner_radius, GFXC.frame_corner_radius, Qt.RelativeSize)
        # super(QFrameItem, self).paint(painter, option, widget)

    def boundingRect(self) -> QRectF:
        """return relative bounding rectangle, given position is set by Track parent as needed
        """
        # from inspect import getouterframes, currentframe
        # level = len(getouterframes(currentframe()))
        # LOG.debug("frame boundingRect depth {}".format(level))
        return self._bounds

    # internal recalculation / realignment

    def update_bounds(self):
        """set size and width based on current scaling
        position is controlled by the track, since we have to be track-relative
        """
        left = 0.0
        top = - GFXC.frame_height / 2
        height = GFXC.frame_height
        width = self._scale.calc_pixel_duration(self._duration)
        LOG.debug("width for {} is {} scene pixels".format(self._duration, width))
        old_bounds = self._bounds
        new_bounds = QRectF(left, top, width, height)
        if (old_bounds is None) or (new_bounds != old_bounds):
            self.prepareGeometryChange()
            self._bounds = new_bounds

    # # handle drag and drop
    # def dragEnterEvent(self, event: QGraphicsSceneDragDropEvent):
    #     event.setAccepted(False)
    #
    # def dragLeaveEvent(self, event: QGraphicsSceneDragDropEvent):
    #     event.setAccepted(False)
    #
    # def dropEvent(self, event: QGraphicsSceneDragDropEvent):
    #     event.setAccepted(False)

    # handle clicking
    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        LOG.debug("QFrameItem mouse-down")
        return super(QFrameItem, self).mousePressEvent(event)

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        LOG.debug("QFrameItem mouse-up")
        return super(QFrameItem, self).mouseReleaseEvent(event)

    def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent):
        LOG.debug("contextMenuEvent")
        trk = self._track()
        track = trk.track
        scene = trk.scene_
        if not _perform_context_menu(event, scene, track, self._uuid):
            return super(QFrameItem, self).contextMenuEvent(event)

class QTimeRulerItem(QGraphicsRectItem):
    """A ruler object showing the time dimension.

    An instance is at the top, bottom, or both ends of the Scene.

    """

    def __init__(self):
        super(QTimeRulerItem, self).__init__()


from django.utils.translation import ugettext as _

import activity
from activity import ActivityForModel
from activity.feeds import ChangesOnItemFeed
from localwiki.utils.urlresolvers import reverse

from models import Page, PageFile, slugify

class PageChanges(ActivityForModel):
    classname = 'page'

    def queryset(self, start_at=None):
        if self.region:
            qs = Page.versions.filter(region=self.region)
        else:
            qs = Page.versions.all()

        if start_at:
            qs = qs.filter(version_info__date__gte=start_at)

        return qs.defer('content')

    def page(self, obj):
        return obj

activity.register(PageChanges)

class PageFileChanges(ActivityForModel):
    classname = 'file'

    def queryset(self, start_at=None):
        if self.region:
            qs = PageFile.versions.filter(region=self.region)
        else:
            qs = PageFile.versions.all()

        if start_at:
            qs = qs.filter(version_info__date__gte=start_at)
        return qs

    def page(self, obj):
        try:
            page = Page.objects.get(slug=obj.slug, region=obj.region)
        except Page.DoesNotExist:
            page = Page(slug=obj.slug, region=obj.region, name=obj.slug.capitalize())
        return page

    def title(self, obj):
        return _('File %(filename)s on page "%(pagename)s"') % {
            'filename': obj.name, 'pagename': self.page(obj).name}

    def diff_url(self, obj):
        return reverse('pages:file-compare-dates', kwargs={
            'slug': self.page(obj).pretty_slug,
            'region': obj.region.slug,
            'date1': obj.version_info.date,
            'file': obj.name,
        })

    def as_of_url(self, obj):
        return reverse('pages:file-as_of_date', kwargs={
            'slug': self.page(obj).pretty_slug,
            'region': obj.region.slug,
            'date': obj.version_info.date,
            'file': obj.name,
        })

activity.register(PageFileChanges)

class PageChangesFeed(ChangesOnItemFeed):
    activity_class = PageChanges

    def get_object(self, request, region='', slug=''):
        self.setup_region(region)

        obj = Page(slug=slugify(slug), region=self.region)
        obj.title = obj.versions.most_recent().name
        obj.page = obj
        return obj

class PageFileChangesFeed(ChangesOnItemFeed):
    activity_class = PageFileChanges

    def get_object(self, request, region='', slug='', file=''):
        self.setup_region(region)

        obj = PageFile(slug=slugify(slug), region=self.region, name=file)
        page = Page(slug=slugify(slug), region=self.region)
        obj.page = page.versions.most_recent()
        obj.title = _('File %(filename)s on page "%(pagename)s"') % {
            'filename': obj.name, 'pagename': obj.page.name}
        return obj


import utils
import context
import operator
import view.context as ctx

class Keymap(dict):
    def __init__(self, name, *a, **k):
        dict.__init__(self, *a, **k)
        self.NAME = name
        KeymapManager.register_keymap(self)

class KeymapManager():
    """KeymapManager

    Instance of this class helps to map OS specific events
    to application methods.
    Application actions (aka Operators) are mapped on events,
    using a function as key. This function is used to match events.
    Another feature is the multi maps handling: at any moments
    the KeymapManager instance can uses another map,
    that permit to implement modal handling.

    Requiers 2.7
    """

    __metaclass__ = utils.MetaSingleton
    __kmd = {} # all registered keymaps

    def __init__(self, default):
        self._km = None # active keymap
        self._filters = None # current keymap's keys view used as filters
        self._mapstack = []
        self.use(default)
        self._locals = {}

    def process(self, *args):
        assert self._km is not None # need a valid keymap
        self._locals['event'] = args[1]
        done = self._process(self._km, self._filters, self._locals, args)
        if not done and self._mapstack:
            km = KeymapManager.__kmd.get(self._mapstack[-1])
            if km:
                done = self._process(km, km.viewkeys(), self._locals, args)
        return done

    def _process(self, km, filters, locals, args):
        opl = [ km[f] for f in filters if f(*args) ]
        for op in opl:
            eval(op, operator.ope_globals, locals)
        return bool(opl)

    def use(self, name):
        m = KeymapManager.__kmd.get(name, self._km)
        if m is not self._km:
            ctx.keymap = self
            self._km = m
            self._filters = m.viewkeys()

    def use_default(self):
        assert self._default is not None
        self.use(self._default)

    def push(self, name):
        self._mapstack.append(self._km.NAME)
        self.use(name)

    def pop(self):
        if self._mapstack:
            self.use(self._mapstack.pop())

    @classmethod
    def register_keymap(cls, km):
        """Transform each keymap dictionnary key (string)
        into a callable with following prototype:

        "lambda evt_type, evt: bool"

        Note: this operation destroy original contents of the keymap.
        """

        for k in km.keys():
            km[eval("lambda evt_type, evt:" + k)] = compile(km.pop(k), 'keymap', 'exec')
        cls.__kmd[km.NAME] = km


# Build Model
from transformers import BertModel
import torch.nn as nn

class BERT_Model(nn.Module):
  def __init__(self):
    super(BERT_Model, self).__init__()
    self.bert = BertModel.from_pretrained('bert-base-uncased') # use bert pretrained on hugging face
    self.drop = nn.Dropout(p=0.1) # dropout layer to avoid overfit

    # after bert, we make a fully connect layer to do the final regression output
    self.ffc = nn.Sequential(
      nn.Linear(768, 512),
      nn.ReLU(),
      nn.Linear(512, 256),
      nn.ReLU(),
      nn.Linear(256, 2),
    )

  def __call__(self, input_ids, attention_mask):
    output = self.bert(input_ids, attention_mask)
    output = output.pooler_output
    output = self.drop(output)
    output = self.ffc(output)
    return output.double()


import geopandas as gpd
import geojsonio as gjio
import requests, json
import pandas as pd

api_key = '***************************************'
zips = gpd.read_file('zip_bounds.geojson')
url = "https://maps.googleapis.com/maps/api/place/textsearch/json?"

INPUT_STRING = 'chinese restaurant '

frames = []
for zip in zips['postalCode']:
    query = INPUT_STRING + zip
    print(query)
    r = requests.get(url + 'query=' + query + '&key=' + api_key)
    results = r.json()
    df = pd.DataFrame(results['results'])
    frames.append(df)
    print(len(frames))

final_df = pd.concat(frames)
final_df.to_csv('chinese_data_nyc.csv')


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

def Gaussian_pyrimid(img):
    copy_img = img.copy()

    titles = ['Original', 'Level1', 'Level2', 'Level3']
    g_down = []
    g_up = []

    g_down.append(copy_img)

    for i in range(3):
        temp1 = pyrDown(copy_img)
        g_down.append(temp1)
        copy_img = temp1

    imshow('Level3', copy_img)

    for i in range(4):
        imshow(titles[i], g_down[i])

    waitKey(0)
    destroyAllWindows()

def Gaussian_upsampling(img):
    copy_img = img.copy()

    titles = ['Original', 'Level1', 'Level2', 'Level3']
    g_down = []
    g_up = []

    g_down.append(copy_img)

    for i in range(3):
        temp1 = pyrDown(copy_img)
        g_down.append(temp1)
        copy_img = temp1

    imshow('Level3', copy_img)

    for i in range(3):
        copy_img = g_down[i+1]
        temp1 = pyrUp(copy_img)
        g_up.append(temp1)

    for i in range(3):
        imshow(titles[i], g_up[i])

    waitKey(0)
    destroyAllWindows()

def Laplacian_pyramid(img):
    copy_img = img.copy()

    titles = ['Original', 'Level1', 'Level2', 'Level3']
    g_down = []
    g_up = []
    image_append = []

    g_down.append(copy_img)
    image_append.append(copy_img.shape)

    for i in range(3):
        temp1 = pyrDown(copy_img)
        g_down.append(temp1)
        image_append.append(temp1.shape)
        copy_img = temp1

    for i in range(3):
        copy_img = g_down[i+1]
        temp1 = pyrUp(copy_img)
        copy_img = resize(temp1, dsize=(image_append[i][1], image_append[i][0]), interpolation=INTER_CUBIC)
        g_up.append(copy_img)

    for i in range(3):
        copy_img = subtract(g_down[i], g_up[i])
        imshow(titles[i], copy_img)

        #cc = add(g_up[i], copy_img)
        #imshow(titles[i], cc)
        #imshow("titles[i]", img)

    waitKey(0)
    destroyAllWindows()

def fourier(img):
    f = np.fft.fft2(img)
    fshift = np.fft.fftshift(f) #주파수 영역 한군데로 모으기
    m_specturm = 20 * np.log(np.abs(fshift))

    plt.subplot(121), plt.imshow(img, cmap='gray')
    plt.title('input image'), plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(m_specturm, cmap='gray')
    plt.title('m_specturm'), plt.xticks([]), plt.yticks([])
    plt.show()
    #저주파 영역 60*60

    #imshow('title', img)
    #imshow('changed', m_specturm)
    #waitKey(0)
    #destroyAllWindows()

def LPF(img):
    dft = cv2.dft(np.float32(img), flags=DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)

    magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0], dft_shift[:,:,1]))

if __name__ == "__main__":
    #IMREAD_GRAYSCALE
    img = imread('123.jpg', IMREAD_GRAYSCALE)
    #Gaussian_pyrimid(img)
    #Gaussian_upsampling(img)
    #Laplacian_pyramid(img)
    #fourier(img)
    LPF(img)


import datetime

import pandas as pd

from . import util

# Previous year OES gets released in May
if datetime.date.today().month > 6:
    CUR_YEAR = datetime.date.today().year - 1
else:
    CUR_YEAR = datetime.date.today().year - 2

def _get_zip_urls(year):
    """Return urls for zip files based on pattern.

    Parameters
    ----------
    year : string
        4 digit year in string format 'YYYY'.

    Returns
    -------
    dict
    """

    suffix = f'oesm{year[2:]}'
    code = 4

    return {
        'national': f'https://www.bls.gov/oes/special.requests/{suffix}'
            'nat.zip',
        'state': f'https://www.bls.gov/oes/special.requests/{suffix}'
            'st.zip',
        'area': f'https://www.bls.gov/oes/special.requests/{suffix}ma.zip',
        'industry': f'https://www.bls.gov/oes/special.requests/{suffix}in'
            f'{code}.zip'}

def _get_filenames(year, industry_scope=None):
    """Return filenames for oes files based on pattern.

    Parameters
    ----------
    year : string
        4 digit year in string format 'YYYY'.

    Returns
    -------
    dict.

    """
    if industry_scope == None:
        prefix = 'natsector'
    elif (industry_scope == 5) or (industry_scope == 6):
        prefix = 'nat5d_6d'
    else:
        prefix = 'nat{}d'.format(industry_scope)

    return {
        'national': f'oesm{year[2:]}nat/national_M{year}_dl.xlsx',
        'state': f'oesm{year[2:]}st/state_M{year}_dl.xlsx',
        'metros': f'oesm{year[2:]}ma/MSA_M{year}_dl.xlsx',
        'metros-divisions': f'oesm{year[2:]}ma/aMSA_M{year}_dl.xlsx' ,
        'non-metros': f'oesm{year[2:]}ma/BOS_M{year}_dl.xlsx',
        'industry': f'oesm{year[2:]}in4/{prefix}_M{year}_dl.xlsx'
    }

def get_data(year=CUR_YEAR, cut_by='national', area_focus=None,
    industry_scope=None,rtype='dataframe', set_missing_figures_as_na=True):
    """Get a dataset from the Occupational Employment Statistics (OES).

    The OES database contains statistics for various occupations.
    Primary statistics include wage, salary and total employment by occupation.

    Default behavior returns occupational data at a national
    level. This behavior can be overridden by adding options.

    Intended usage is to return a pandas dataframe for easy data
    analysis. This can be overwridden to return a primitive i/o object for
    different data processing decisions.

    Parameters
    ----------
    year : str
        year must be in form 'YYYY'. This package only supports OES after 2014
    cut_by : str
        Get occupation figures that are either cut by location
        or industry. Only three options are allowed: `state`, `area`,
        or `industry`. If not specified, default behavior is
        to return nationwide figures.
    area_focus : str
        If `cut_by = area` then an `area_focus` must also be
        specifed. Options include: metros, metro-divisions, or non-metros
    industry_scope : int, or None
        If `cut_by = industry` then an `industry_scope` must also be specified.
        Options include 3, 4, 5 or 6 digit scope.
        If not specified, then the default is top level sectors (i.e 2 digit).
        Industry scope describes the level of industry granularity. Higher the
        scope, the more granular the industries will be.
    set_missing_figures_as_na : bool, Default = True
        BLS uses ['*', '**', '#', '~'] to designate missing non-numeric figures
        in columns that have numeric statistics, such as hourly wage. This
        mismatch converts the whole column to an object and not a number type.
        By keeping this argument to True, this symbols will get converted
        to NA values allowing these columns to be calculated and treated as
        numbers.
    rtype : str
        Default behavior is to return a pandas dataframe.
        If you want a primitive i/o object that supports open(),
        then specify rtype='io'.

    Returns
    -------
    pd.Dataframe, or io file like object

    Usage:

    >>> df = get_data(2017, cut_by='state')
    >>> df.head()
       AREA  ST    STATE OCC_CODE  ...   A_PCT75 A_PCT90 ANNUAL HOURLY
    0     1  AL  Alabama  00-0000  ...     52020   78690    NaN    NaN
    1     1  AL  Alabama  11-0000  ...    133360  188860    NaN    NaN
    2     1  AL  Alabama  11-1011  ...         #       #    NaN    NaN
    3     1  AL  Alabama  11-1021  ...    147860       #    NaN    NaN
    4     1  AL  Alabama  11-1031  ...     24630   47510   True    NaN

    [5 rows x 25 columns]

    """
    if int(year) < 2014:
        raise ValueError(f'Year:{year}. OES only supports years post 2014.')
    if int(year) > CUR_YEAR:
        raise ValueError(f'Year:{year}. OES data not available yet.')

    if industry_scope != None and industry_scope not in range(3, 7):
        raise ValueError(f'Industry scope:{industry_scope}. '
            'Not a valid industry scope. Options include ints from 3 to 6')

    OES_ZIP_URLS = _get_zip_urls(str(year))
    OES_FILENAMES = _get_filenames(str(year), industry_scope)

    zip_url = OES_ZIP_URLS.get(cut_by)
    if zip_url == None:
        raise ValueError('"{}" is not a valid cut_by option\n' \
            'valid options include:\n{}' \
            .format(cut_by, [k for k in OES_ZIP_URLS.keys()]))

    if cut_by == 'area':
        filename = OES_FILENAMES.get(area_focus)
        if filename == None:
            raise ValueError('"{}" is not a valid area focus\n' \
                'valid options include:\n{}' \
                .format(area_focus, ['metros', 'metros-divisions', 'non-metros']))
    else:
        filename = OES_FILENAMES.get(cut_by)

    fp = util.get_file(zip_url, filename)

    if set_missing_figures_as_na:
        na_values = ['*', '**', '#', '~']
    else:
        na_values = []

    if rtype == 'dataframe':
        return pd.read_excel(fp, na_values = na_values)
    elif rtype == 'io':
        return fp
    else:
        raise ValueError('{} is not a valid return type\n' \
            'valid return types are `dataframe` or `io`' \
            .format(rtype))


# -*- coding: utf-8 -*-
"""
Created on Wed Apr 19 10:30:08 2017

@author: jbrugger
"""

import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)

#plotdata = [{"West" : [1,2,3,4,5]}, {"East" : [6,7,8,9,10]}]
plotdata = [[1,2,3,4,5], [6,7,8,9,10]]

ax.boxplot(plotdata)
plt.show()


from uuid import UUID

from gql import gql
from gql.client import AsyncClientSession
from pydantic import BaseModel
from pydantic import parse_obj_as
from structlog import get_logger

from os2mo_init.config import ConfigFacet

logger = get_logger(__name__)

class Class(BaseModel):
    uuid: UUID
    user_key: str
    name: str
    scope: str | None

class Facet(BaseModel):
    uuid: UUID
    user_key: str
    classes: list[Class]

async def get_classes(graphql_session: AsyncClientSession) -> list[Facet]:
    """
    Get all classes in OS2mo.

    Args:
        graphql_session: MO GraphQL client session.

    Returns:
        List of Facets, which contain Classes.
    """
    logger.info("Getting classes")
    query = gql(
        """
        query ClassesQuery {
          facets {
            objects {
              current {
                uuid
                user_key
                classes {
                  uuid
                  user_key
                  name
                  scope
                }
              }
            }
          }
        }
    """
    )
    result = await graphql_session.execute(query)
    return parse_obj_as(
        list[Facet], [f["current"] for f in result["facets"]["objects"]]
    )

async def ensure_classes(
    graphql_session: AsyncClientSession,
    config_classes: dict[str, ConfigFacet],
) -> None:
    """Ensure that the given classes exists.

    Args:
        graphql_session: MO GraphQL client session.
        config_classes: Desired facets and their classes.
    """
    logger.info("Ensuring classes", classes=config_classes)

    existing_classes = await get_classes(graphql_session)
    logger.debug("Existing classes", existing=existing_classes)
    facet_uuids = {f.user_key: f.uuid for f in existing_classes}
    existing_classes_by_user_key = {
        f.user_key: {c.user_key: c for c in f.classes} for f in existing_classes
    }

    create_mutation = gql(
        """
        mutation CreateClassMutation(
          $facet_uuid: UUID!,
          $user_key: String!
          $name: String!,
          $scope: String!,
        ) {
          class_create(
            input: {
              facet_uuid: $facet_uuid,
              user_key: $user_key,
              name: $name,
              scope: $scope,
            }
          ) {
            uuid
          }
        }
        """
    )
    update_mutation = gql(
        """
        mutation UpdateClassMutation(
          $facet_uuid: UUID!,
          $uuid: UUID!,
          $user_key: String!,
          $name: String!,
          $scope: String!
        ) {
          class_update(
            uuid: $uuid
            input: {
              facet_uuid: $facet_uuid,
              user_key: $user_key
              name: $name,
              scope: $scope,
            }
          ) {
            uuid
          }
        }
    """
    )

    for facet_user_key, classes in config_classes.items():
        for class_user_key, class_data in classes.items():
            try:
                existing = existing_classes_by_user_key[facet_user_key][class_user_key]
            except KeyError:
                logger.info("Creating class", data=class_data)
                await graphql_session.execute(
                    create_mutation,
                    {
                        "facet_uuid": str(facet_uuids[facet_user_key]),
                        "user_key": class_user_key,
                        "name": class_data.title,
                        "scope": class_data.scope,
                    },
                )
                continue
            if existing.name != class_data.title or existing.scope != class_data.scope:
                logger.info("Updating class", data=class_data)
                await graphql_session.execute(
                    update_mutation,
                    {
                        "facet_uuid": str(facet_uuids[facet_user_key]),
                        "uuid": str(existing.uuid),
                        "user_key": class_user_key,
                        "name": class_data.title,
                        "scope": class_data.scope,
                    },
                )


from celery_sqlalchemy.model import sqlalchemy_1_4

from celery_sqlalchemy.schema import TypeMap

# system imports
from typing import Any
from typing import List

# dependency imports
from pytest import mark

from sqlalchemy.sql import sqltypes

@mark.parametrize(
    "type",
    [
        [sqltypes.ARRAY, "array_from_json", "array_params", "array_to_json"],
        [
            sqltypes.BigInteger,
            "biginteger_from_json",
            "biginteger_params",
            "biginteger_to_json",
        ],
        [sqltypes.Boolean, "boolean_from_json", "boolean_params", "boolean_to_json"],
        [sqltypes.Date, "date_from_json", "date_params", "date_to_json"],
        [
            sqltypes.DateTime,
            "datetime_from_json",
            "datetime_params",
            "datetime_to_json",
        ],
        [sqltypes.Enum, "enum_from_json", "enum_params", "enum_to_json"],
        [sqltypes.Float, "float_from_json", "float_params", "float_to_json"],
        [sqltypes.Integer, "integer_from_json", "integer_params", "integer_to_json"],
        [
            sqltypes.Interval,
            "interval_from_json",
            "interval_params",
            "interval_to_json",
        ],
        [
            sqltypes.LargeBinary,
            "largebinary_from_json",
            "largebinary_params",
            "largebinary_to_json",
        ],
        [sqltypes.JSON, "json_from_json", "json_params", "json_to_json"],
        [sqltypes.Numeric, "numeric_from_json", "numeric_params", "numeric_to_json"],
        [
            sqltypes.SmallInteger,
            "smallinteger_from_json",
            "smallinteger_params",
            "smallinteger_to_json",
        ],
        [sqltypes.String, "string_from_json", "string_params", "string_to_json"],
        [sqltypes.Text, "text_from_json", "text_params", "text_to_json"],
        [sqltypes.Time, "time_from_json", "time_params", "time_to_json"],
        [sqltypes.Unicode, "unicode_from_json", "unicode_params", "unicode_to_json"],
        [
            sqltypes.UnicodeText,
            "unicodetext_from_json",
            "unicodetext_params",
            "unicodetext_to_json",
        ],
    ],
)
def test_type_maps(type: List[Any]) -> None:
    assert sqlalchemy_1_4.type_maps[type[0]] == TypeMap(
        from_json=type[1], params=type[2], to_json=type[3]
    )