from collections import OrderedDict
import itertools
import logging
import numpy as np
import numba as nb
from scipy.optimize import linear_sum_assignment
from scipy.spatial.distance import cdist
from cython_bbox import bbox_overlaps

from .track import Track
from .flow import Flow
from .kalman_filter import MeasType, KalmanFilter
from .utils.rect import as_rect, to_tlbr, iom

LOGGER = logging.getLogger(__name__)
CHI_SQ_INV_95 = 9.4877 # 0.95 quantile of chi-square distribution
INF_COST = 1e5

class MultiTracker:
    """
    Uses optical flow and Kalman filter to track multiple objects and
    associates detections to tracklets based on motion and appearance.
    Parameters
    ----------
    size : (int, int)
        Width and height of each frame.
    dt : float
        Time interval in seconds between each frame.
    metric : string
        Feature distance metric to associate tracklets. Usually
        `euclidean` or `cosine`.
    config : Dict
        Tracker hyperparameters.
    """

    def __init__(self, size, dt, metric, config):
        self.size = size
        self.metric = metric
        self.max_age = config['max_age']
        self.age_factor = config['age_factor']
        self.motion_weight = config['motion_weight']
        self.max_feat_cost = config['max_feat_cost']
        self.max_reid_cost = config['max_reid_cost']
        self.iou_thresh = config['iou_thresh']
        self.duplicate_iou = config['duplicate_iou']
        self.conf_thresh = config['conf_thresh']
        self.lost_buf_size = config['lost_buf_size']

        self.next_id = 1
        self.tracks = {}
        self.lost = OrderedDict()
        self.kf = KalmanFilter(dt, config['kalman_filter'])
        self.flow = Flow(self.size, config['flow'])
        self.frame_rect = to_tlbr((0, 0, *self.size))

        self.flow_bboxes = {}
        self.homography = None

    def initiate(self, frame, detections):
        """
        Initializes the tracker from detections in the first frame.
        Parameters
        ----------
        frame : ndarray
            Initial frame.
        detections : recarray[DET_DTYPE]
            Record array of N detections.
        """
        if self.tracks:
            self.tracks.clear()
        self.flow.initiate(frame)
        for det in detections:
            state = self.kf.initiate(det.tlbr)
            new_trk = Track(0, self.next_id, det.tlbr, state, det.label)
            self.tracks[self.next_id] = new_trk
            LOGGER.debug('Detected: %s', new_trk)
            self.next_id += 1

    def track(self, frame):
        """
        Convenience function that combines `compute_flow` and `apply_kalman`.
        Parameters
        ----------
        frame : ndarray
            The next frame.
        """
        self.compute_flow(frame)
        self.apply_kalman()

    def compute_flow(self, frame):
        """
        Computes optical flow to estimate tracklet positions and camera motion.
        Parameters
        ----------
        frame : ndarray
            The next frame.
        """
        active_tracks = [track for track in self.tracks.values() if track.active]
        self.flow_bboxes, self.homography = self.flow.predict(frame, active_tracks)
        if self.homography is None:
            # clear tracks when camera motion cannot be estimated
            self.tracks.clear()

    def apply_kalman(self):
        """
        Performs kalman filter prediction and update from flow measurements.
        The function should be called after `compute_flow`.
        """
        for trk_id, track in list(self.tracks.items()):
            mean, cov = track.state
            mean, cov = self.kf.warp(mean, cov, self.homography)
            mean, cov = self.kf.predict(mean, cov)
            if trk_id in self.flow_bboxes:
                flow_tlbr = self.flow_bboxes[trk_id]
                # give large flow uncertainty for occluded tracks
                # usually these with high age and low inlier ratio
                std_multiplier = max(self.age_factor * track.age, 1) / track.inlier_ratio
                mean, cov = self.kf.update(mean, cov, flow_tlbr, MeasType.FLOW, std_multiplier)
            next_tlbr = as_rect(mean[:4])
            track.update(next_tlbr, (mean, cov))
            if iom(next_tlbr, self.frame_rect) < 0.5:
                if track.confirmed:
                    LOGGER.info('Out: %s', track)
                    self._mark_lost(trk_id)
                else:
                    del self.tracks[trk_id]

    def update(self, frame_id, detections, embeddings):
        """
        Associates detections to tracklets based on motion and feature embeddings.
        Parameters
        ----------
        frame_id : int
            The next frame ID.
        detections : recarray[DET_DTYPE]
            Record array of N detections.
        embeddings : ndarray
            NxM matrix of N extracted embeddings with dimension M.
        """
        det_ids = list(range(len(detections)))
        confirmed = [trk_id for trk_id, track in self.tracks.items() if track.confirmed]
        unconfirmed = [trk_id for trk_id, track in self.tracks.items() if not track.confirmed]

        # association with motion and embeddings
        cost = self._matching_cost(confirmed, detections, embeddings)
        matches1, u_trk_ids1, u_det_ids = self._linear_assignment(cost, confirmed, det_ids)

        # 2nd association with IoU
        active = [trk_id for trk_id in u_trk_ids1 if self.tracks[trk_id].active]
        u_trk_ids1 = [trk_id for trk_id in u_trk_ids1 if not self.tracks[trk_id].active]
        u_detections = detections[u_det_ids]
        cost = self._iou_cost(active, u_detections)
        matches2, u_trk_ids2, u_det_ids = self._linear_assignment(cost, active, u_det_ids, True)

        # 3rd association with unconfirmed tracks
        u_detections = detections[u_det_ids]
        cost = self._iou_cost(unconfirmed, u_detections)
        matches3, u_trk_ids3, u_det_ids = self._linear_assignment(cost, unconfirmed,
                                                                  u_det_ids, True)

        # re-id with lost tracks
        lost_ids = list(self.lost.keys())
        u_det_ids = [det_id for det_id in u_det_ids if detections[det_id].conf >= self.conf_thresh]
        u_detections, u_embeddings = detections[u_det_ids], embeddings[u_det_ids]
        cost = self._reid_cost(u_detections, u_embeddings)
        reid_matches, _, u_det_ids = self._linear_assignment(cost, lost_ids, u_det_ids)

        matches = itertools.chain(matches1, matches2, matches3)
        u_trk_ids = itertools.chain(u_trk_ids1, u_trk_ids2, u_trk_ids3)
        updated, aged = [], []

        # update matched tracks
        for trk_id, det_id in matches:
            track = self.tracks[trk_id]
            det = detections[det_id]
            mean, cov = self.kf.update(*track.state, det.tlbr, MeasType.DETECTOR)
            next_tlbr = as_rect(mean[:4])
            track.update(next_tlbr, (mean, cov), embeddings[det_id])
            if track.hits == 1:
                LOGGER.info('Found: %s', track)
            if iom(next_tlbr, self.frame_rect) < 0.5:
                LOGGER.info('Out: %s', track)
                self._mark_lost(trk_id)
            else:
                updated.append(trk_id)

        # reactivate matched lost tracks
        for trk_id, det_id in reid_matches:
            track = self.lost[trk_id]
            det = detections[det_id]
            LOGGER.info('Re-identified: %s', track)
            state = self.kf.initiate(det.tlbr)
            track.reactivate(frame_id, det.tlbr, state, embeddings[det_id])
            self.tracks[trk_id] = track
            del self.lost[trk_id]
            updated.append(trk_id)

        # clean up lost tracks
        for trk_id in u_trk_ids:
            track = self.tracks[trk_id]
            if not track.confirmed:
                LOGGER.debug('Unconfirmed: %s', track)
                del self.tracks[trk_id]
                continue
            track.mark_missed()
            if track.age > self.max_age:
                LOGGER.info('Lost: %s', track)
                self._mark_lost(trk_id)
            else:
                aged.append(trk_id)

        # register new detections
        for det_id in u_det_ids:
            det = detections[det_id]
            state = self.kf.initiate(det.tlbr)
            new_trk = Track(frame_id, self.next_id, det.tlbr, state, det.label)
            self.tracks[self.next_id] = new_trk
            LOGGER.debug('Detected: %s', new_trk)
            updated.append(self.next_id)
            self.next_id += 1

        # remove duplicate tracks
        self._remove_duplicate(updated, aged)

    def _mark_lost(self, trk_id):
        self.lost[trk_id] = self.tracks[trk_id]
        if len(self.lost) > self.lost_buf_size:
            self.lost.popitem(last=False)
        del self.tracks[trk_id]

    def _matching_cost(self, trk_ids, detections, embeddings):
        if len(trk_ids) == 0 or len(detections) == 0:
            return np.empty((len(trk_ids), len(detections)))

        features = [self.tracks[trk_id].smooth_feature for trk_id in trk_ids]
        cost = cdist(features, embeddings, self.metric)
        for i, trk_id in enumerate(trk_ids):
            track = self.tracks[trk_id]
            motion_dist = self.kf.motion_distance(*track.state, detections.tlbr)
            cost[i] = self._fuse_motion(cost[i], motion_dist, track.label, detections.label,
                                        self.max_feat_cost, self.motion_weight)
        return cost

    def _iou_cost(self, trk_ids, detections):
        if len(trk_ids) == 0 or len(detections) == 0:
            return np.empty((len(trk_ids), len(detections)))

        # make sure associated pair has the same class label
        trk_labels = np.array([self.tracks[trk_id].label for trk_id in trk_ids])
        trk_bboxes = np.array([self.tracks[trk_id].tlbr for trk_id in trk_ids])
        det_bboxes = detections.tlbr
        ious = bbox_overlaps(trk_bboxes, det_bboxes)
        ious = self._gate_cost(ious, trk_labels, detections.label, self.iou_thresh, True)
        return ious

    def _reid_cost(self, detections, embeddings):
        if len(self.lost) == 0 or len(detections) == 0:
            return np.empty((len(self.lost), len(detections)))

        trk_labels = np.array([track.label for track in self.lost.values()])
        features = [track.smooth_feature for track in self.lost.values()]
        cost = cdist(features, embeddings, self.metric)
        cost = self._gate_cost(cost, trk_labels, detections.label, self.max_reid_cost)
        return cost

    def _remove_duplicate(self, updated, aged):
        if len(updated) == 0 or len(aged) == 0:
            return

        updated_bboxes = np.array([self.tracks[trk_id].tlbr for trk_id in updated])
        aged_bboxes = np.array([self.tracks[trk_id].tlbr for trk_id in aged])

        ious = bbox_overlaps(updated_bboxes, aged_bboxes)
        idx = np.where(ious >= self.duplicate_iou)
        dup_ids = set()
        for row, col in zip(*idx):
            updated_id, aged_id = updated[row], aged[col]
            if self.tracks[updated_id].start_frame <= self.tracks[aged_id].start_frame:
                dup_ids.add(aged_id)
            else:
                dup_ids.add(updated_id)
        for trk_id in dup_ids:
            LOGGER.debug('Duplicate: %s', self.tracks[trk_id])
            del self.tracks[trk_id]

    @staticmethod
    def _linear_assignment(cost, trk_ids, det_ids, maximize=False):
        rows, cols = linear_sum_assignment(cost, maximize)
        unmatched_rows = list(set(range(cost.shape[0])) - set(rows))
        unmatched_cols = list(set(range(cost.shape[1])) - set(cols))
        unmatched_trk_ids = [trk_ids[row] for row in unmatched_rows]
        unmatched_det_ids = [det_ids[col] for col in unmatched_cols]
        matches = []
        if not maximize:
            for row, col in zip(rows, cols):
                if cost[row, col] < INF_COST:
                    matches.append((trk_ids[row], det_ids[col]))
                else:
                    unmatched_trk_ids.append(trk_ids[row])
                    unmatched_det_ids.append(det_ids[col])
        else:
            for row, col in zip(rows, cols):
                if cost[row, col] > 0:
                    matches.append((trk_ids[row], det_ids[col]))
                else:
                    unmatched_trk_ids.append(trk_ids[row])
                    unmatched_det_ids.append(det_ids[col])
        return matches, unmatched_trk_ids, unmatched_det_ids

    @staticmethod
    @nb.njit(fastmath=True, cache=True)
    def _fuse_motion(cost, motion_dist, label, det_labels, max_cost, weight):
        gate = (cost > max_cost) | (motion_dist > CHI_SQ_INV_95) | (label != det_labels)
        cost = (1 - weight) * cost + weight * motion_dist
        cost[gate] = INF_COST
        return cost

    @staticmethod
    @nb.njit(parallel=True, fastmath=True, cache=True)
    def _gate_cost(cost, trk_labels, det_labels, thresh, maximize=False):
        for i in nb.prange(len(cost)):
            if maximize:
                gate = (cost[i] < thresh) | (trk_labels[i] != det_labels)
                cost[i][gate] = 0
            else:
                gate = (cost[i] > thresh) | (trk_labels[i] != det_labels)
                cost[i][gate] = INF_COST
        return cost


from pyaudio import PyAudio, paInt16
from datetime import datetime
import wave
# from tkinter import *

# define of params
NUM_SAMPLES = 2000
framerate = 8000
channels = 1
sampwidth = 2
# the longest record time
TIME = 30  # 最长录制时间

FILENAME = ''  # 录制文件名
NOW = ''  # 存储录制时间
SAVE = ''
JUDGE = True  # 进行录制结束的判断

def save_wave_file(filename, data):
    '''save the date to the wav file'''
    wf = wave.open(filename, 'wb')
    wf.setnchannels(channels)
    wf.setsampwidth(sampwidth)
    wf.setframerate(framerate)
    wf.writeframes("".join(data))
    wf.close()

def record_wave():
    # open the input of wave
    pa = PyAudio()
    stream = pa.open(format=paInt16, channels=1, rate=framerate, input=True, frames_per_buffer=NUM_SAMPLES)
    save_buffer = []
    count = 0
    global JUDGE
    while JUDGE and count < TIME * 4:
        # read NUM_SAMPLES sampling data
        string_audio_data = stream.read(NUM_SAMPLES)
        save_buffer.append(string_audio_data)
        count += 1
        print(".")

    now = datetime.now().strftime("%Y-%m-%d_%H_%M_%S")
    filename = now + ".wav"
    SAVE = save_wave_file(filename, save_buffer)
    save_buffer = []
    print(filename, "saved")
    global FILENAME, NOW, JUDGE
    FILENAME = filename
    NOW = now

if __name__ == '__main__':
    """先调用recorder中的函数record_wave，然后在录制结束时将recorder中的全局变量JUDGE置为False，即可完成录制。
    """
    record_wave()


# coding:utf-8
from PyQt5.QtWidgets import (QWidget, QLabel, QApplication, QDesktopWidget,
                             QMessageBox)
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtCore import QTimer, Qt
import sys
import cv2
import threading
import MqttSubClient
import GlobalRes
import HeatMap

class HeatMapWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.title = "车站人员热力检测系统"
        GlobalRes.init_heat_map()  # 初始化热力图，避免残留数据的影响
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setWindowIcon(QIcon(GlobalRes.icon_path))
        self.setFixedSize(1794, 695)
        self.center()
        self.label = QLabel(self)
        timer = QTimer(self)
        timer.timeout.connect(self.update_image)
        timer.start(1000)  # 定时器 1s刷新一次数据
        self.update_image()

    # 主窗口居中显示
    def center(self):
        screen = QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width() - size.width()) / 2,
                  (screen.height() - size.height()) / 2)

    # 更新图片
    def update_image(self):
        pixmap = QPixmap(GlobalRes.image_heat_map_path)
        if not pixmap.isNull():
            self.label.setPixmap(pixmap)
            self.label.adjustSize()
            self.resize(pixmap.size())

# mqtt client to connect broker
def loop_mqtt_client():
    client = MqttSubClient.connect_mqtt()
    MqttSubClient.subscribe(client)
    client.loop_forever()

# mqtt thread to accept message from broker and update datas and heat map
def mqtt_thread(func):
    mqtt_thread = threading.Thread(target=func, name='LoopThread')
    mqtt_thread.setDaemon(True)  # 设置守护进程, 在窗口关闭时，断开 Mqtt连接
    mqtt_thread.start()

if __name__ == '__main__':
    '''开启一个新线程去做连接服务器'''
    mqtt_thread(loop_mqtt_client)
    '''展示热力图'''
    app = QApplication(sys.argv)
    heat_map_ex = HeatMapWindow()
    heat_map_ex.show()
    sys.exit(app.exec_())


import os
import sys

HOLOHOME = os.getenv('HOLOLATTICES_HOME', "")
sys.path.append(HOLOHOME)
import HolographicLattices.control_observables
import HolographicLattices.derivatives
import HolographicLattices.Utilities.GridUtilities
import HolographicLattices.Options.Options
import HolographicLattices.import_equations
import numpy as np
import h5py
import HolographicLattices.nonlinear_solver
import time
import itertools
import scipy
import scipy.sparse
import scipy.sparse.linalg
import logging

def linearFermionOperators(coefficients, setup: 'FermionSetup'):
    logger = logging.getLogger("fermions")
    diffMats = setup.finite_diff_matrices
    # Empty matrices to construct the differentiation matrices in
    # We can see the diff matrix as a huge num_eqs_of_motion x nField block of gridvolume x gridvolume blocks, which
    # can be inverted to find the derivative.

    boundaryIndices, bulkIndices = setup.boundaryIndices, setup.bulkIndices
    boundaryVariables, bulkVariables = setup.getBoundaryBulkInput()
    # Go from Field - Deriv - EOM to field,eom - deriv
    # For most grid sizes, it is the internal evaluation that is by far the slowest, and parallelizing
    # over the data
    numDerivs = len(HolographicLattices.Utilities.GridUtilities.all_derivatives(setup.dims + 1, setup.max_deriv))
    bigDiffMatrix = [[[scipy.sparse.csr_matrix((setup.gridVolume, setup.gridVolume)) for _ in range(setup.num_fields)]
                      for _ in range(setup.num_eqs_of_motion)] for _ in range(numDerivs)]
    for b, block in enumerate(coefficients.internal):
        for i, field in enumerate(block):
            for j, deriv in enumerate(field):
                for k, eom in enumerate(deriv):
                    term_eval = eom(**bulkVariables)
                    import numbers
                    if isinstance(term_eval, numbers.Number):
                        term_eval = np.zeros(len(bulkIndices), dtype=np.complex128) + complex(term_eval)

                    mat = scipy.sparse.coo_matrix((setup.gridVolume, setup.gridVolume))
                    mat.row = bulkIndices
                    mat.col = bulkIndices
                    mat.data = term_eval
                    mat.eliminate_zeros()
                    bigDiffMatrix[b][i][k] += mat.dot(diffMats[j])

    for axis, periodic in enumerate(setup.grid_periodic):
        if not periodic:
            for end in range(2):
                idx = boundaryIndices[axis][end]

                # Evaluate these equations only where they apply. This should reduce
                # computation time somewhat
                variables = boundaryVariables[axis][end]
                coeffs = coefficients.boundary[axis][end]

                # The coefficients are stored as field : deriv : eom for each.
                for b, block in enumerate(coeffs):
                    for i, field in enumerate(block):
                        for j, deriv in enumerate(field):
                            for k, eom in enumerate(deriv):

                                term_eval = eom(**variables)
                                import numbers
                                if isinstance(term_eval, numbers.Number):
                                    term_eval = np.zeros(len(idx), dtype=np.complex128) + complex(term_eval)

                                mat = scipy.sparse.coo_matrix((setup.gridVolume, setup.gridVolume))
                                mat.row = idx
                                mat.col = idx
                                mat.data = term_eval
                                mat.eliminate_zeros()
                                bigDiffMatrix[b][i][k] += mat.dot(diffMats[j])

    # Stack the matrices into a nFxnF form so that it can be inverted
    fermionOperators = [scipy.sparse.csr_matrix((setup.gridVolume, setup.gridVolume)) for _ in range(numDerivs)]
    for b, bigMatrix in enumerate(bigDiffMatrix):
        hStacked = [scipy.sparse.hstack(row) for row in bigMatrix]
        fermionOperators[b] = scipy.sparse.vstack(hStacked, format="csr")
        fermionOperators[b].eliminate_zeros()

    return fermionOperators

# This should just work flawlessly

# @logcall()
def fermionRHS(RHS: 'HolographicLattices.import_equations.Equations', setup: 'FermionSetup'):
    """
    Evaluate the equations of motion for a given set of fields. This does not include the
    right-hand side
    :param fields: Fields to evaluate
    :param eoms_internal: Equations of motion for the points that are not on a boundary
    :param eoms_boundary: boundary equations of motion
    :param setup: Setup of the problem, defined in holo_setup.py
    :return: equations of motion evaluated at each point for each field.
    """
    logger = logging.getLogger("fermions")
    # Empty fields to fill up with equations of motion
    numDerivs = len(HolographicLattices.Utilities.GridUtilities.all_derivatives(setup.dims + 1, setup.max_deriv))
    eomsEvaluated = np.array(
        [[np.zeros(setup.gridVolume, dtype=np.complex128) for _ in range(setup.num_fields)] for _ in range(numDerivs)])

    # These are the indices of the boundaries for 1-d flattened coordinates
    # These are used for indexing into the arrays of points
    # The boundary variable is a list of pairs that for the "0" and "1" end of the boundary,
    # you can interpret that for "left" and "right" or whatever seems appropriate. For periodic
    # boundaries, the indices are None
    boundaryIndices, bulkIndices = setup.boundaryIndices, setup.bulkIndices
    boundaryVariables, bulkVariables = setup.getBoundaryBulkInput()

    # Evaluate and assign the equations of motion to the internal points
    for b, block in enumerate(RHS.internal):
        for i, eom in enumerate(block):
            term_eval = eom(**bulkVariables)
            import numbers
            if isinstance(term_eval, numbers.Number):
                term_eval = np.zeros(len(bulkIndices), dtype=np.complex128) + complex(term_eval)
            eomsEvaluated[b][i][bulkIndices] = term_eval

    # Evaluate and assign the equations of motion to the non-periodic boundary points
    for axis, periodic in enumerate(setup.grid_periodic):
        if not periodic:
            # Each non-periodic direction has two boundaries
            for end in range(2):
                idx = boundaryIndices[axis][end]
                variables = boundaryVariables[axis][end]
                eoms = RHS.boundary[axis][end]
                # Evaluate these equations only where they apply. This should reduce
                # computation time somewhat
                for b, block in enumerate(eoms):
                    for k, eom in enumerate(block):

                        term_eval = eom(**variables)
                        import numbers
                        if isinstance(term_eval, numbers.Number):
                            term_eval = np.zeros(len(idx), dtype=np.complex128) + complex(term_eval)
                        eomsEvaluated[b][k][idx] = term_eval
    return eomsEvaluated

# @logcall()
def readFermionRHS(filename, setup: 'FermionSetup'):
    """
    Read the nonlinear-eoms file.
    :param filename:
    :param setup_data:
    :return:
    """
    try:
        with open(filename, "r") as infile:
            lines = []

            numBlocks = len(HolographicLattices.Utilities.GridUtilities.all_derivatives(setup.dims + 1, setup.max_deriv))
            for line in infile.readlines():
                params = ", ".join((setup.backgroundName, *setup.grid_names))
                l = " ".join(("lambda", params, ":", line))
                fun = eval(l, {**setup.constants, **setup.functions})
                lines.append(fun)

            lines = np.array(lines)
            lines = lines.reshape(numBlocks, setup.num_fields)
            return lines
    except IOError as e:
        logger = logging.getLogger("fermions")
        logger.error(f"Error reading file: {filename}. Error:{e}")
        raise e

# @logcall()
def readFermionCoefficients(filename, setup: 'FermionSetup'):
    """
    Rewritten import_expressions that is more readable

    :param filename: Name of file to read
    :return: Array of N_eom x num_fields x N_derivs elements with the equations in them
    """
    logger = logging.getLogger("fermions")
    try:
        with open(filename, "r") as infile:

            # Strip any whitespace for parsing
            lines = [line.strip() for line in infile.readlines() if len(line.strip()) > 0]

            nEOM = setup.num_eqs_of_motion
            nFields = setup.num_fields
            maxDeriv = setup.max_deriv
            dims = setup.dims

            # All possible different derivatives
            numBlocks = len(HolographicLattices.Utilities.GridUtilities.all_derivatives(dims + 1, maxDeriv))
            numDerivs = len(HolographicLattices.Utilities.GridUtilities.all_derivatives(dims, maxDeriv))

            terms_in_eom = [[[[lambda: 0 for _ in range(nFields)] for _ in range(numDerivs)] for _ in range(nEOM)] for _
                            in range(numBlocks)]
            # this is for iterating over all eoms, fields and derivatives
            indices = itertools.product(range(numBlocks), range(nEOM), range(numDerivs), range(nFields))
            eval_dict = {**setup.constants, **setup.functions}
            for (block, eom, deriv, field), line in zip(indices, lines):
                params = ", ".join((setup.backgroundName, *setup.grid_names))
                l = " ".join(("lambda", params, ":", line))
                terms_in_eom[block][eom][deriv][field] = eval(l, eval_dict)
            return np.array(terms_in_eom)

    except IOError as e:
        logger.error(f"Error reading file: {filename}. Error:{e}")
        raise e

##@logcall()
def loadFermionEquations(setup: 'FermionSetup'):
    coefficientFolder = os.path.realpath(setup.coefficient_folder)
    coefsInternalFName = os.path.join(coefficientFolder, "FermionCoefs_I.txt")
    fermionRHSFName = os.path.join(coefficientFolder, "FermionRHS_I.txt")

    coefsInternal = readFermionCoefficients(coefsInternalFName, setup)
    fermionRHSInternal = readFermionRHS(fermionRHSFName, setup)
    # Everything might be periodic, so initialize to None for each direction
    coefsBdy = [[None, None] for _ in range(setup.dims)]
    fermionBdyRHS = [[None, None] for _ in range(setup.dims)]
    for i, periodic in enumerate(setup.grid_periodic):
        if not periodic:
            for j in range(2):
                bdyCoefsFName = os.path.join(coefficientFolder, f"FermionCoefs_{i}_{j}.txt")
                fermionRHSBdy = os.path.join(coefficientFolder, f"FermionRHS_{i}_{j}.txt")

                boundaryCoefs = readFermionCoefficients(bdyCoefsFName, setup)
                fermionRHSBdy = readFermionRHS(fermionRHSBdy, setup)

                coefsBdy[i][j] = boundaryCoefs
                fermionBdyRHS[i][j] = fermionRHSBdy

    FermionCoefs = HolographicLattices.import_equations.Equations(coefsInternal, coefsBdy)
    FermionRHS = HolographicLattices.import_equations.Equations(fermionRHSInternal, fermionBdyRHS)

    return FermionCoefs, FermionRHS

def getFermionOperator(momentumValues, linearOperatorBlocks, fermionRHSValues, setup: 'FermionSetup'):
    # From the not kx, ky, omega dependent operator blocks and righthand side, contruct the full problem.

    derivs = HolographicLattices.Utilities.GridUtilities.all_derivatives(setup.dims + 1, setup.max_deriv)

    # Have this many blocks
    multiplyingFactors = np.zeros(len(derivs))

    # The first block is always multiplied by 1
    multiplyingFactors[0] = 1
    for i in range(1, len(derivs)):
        deriv = derivs[i]
        if all(x == 0 for x in momentumValues):
            continue
        else:
            prod = 1
            for j, val in enumerate(deriv):
                if val != 0:
                    prod = prod * np.power(momentumValues[j], val)
            multiplyingFactors[i] = prod
    finalOperator = scipy.sparse.csr_matrix((setup.num_fields * setup.gridVolume, setup.num_fields * setup.gridVolume))
    finalRHS = np.zeros(setup.gridVolume * setup.num_fields, dtype=np.complex128)

    for i, factor in enumerate(multiplyingFactors):
        finalOperator += factor * linearOperatorBlocks[i]
        finalRHS += factor * fermionRHSValues[i].ravel()

    return finalOperator, finalRHS

# Example step hook:

##@logcall("INFO")
def exportFermionsHDF5(filename, fermions, setup: 'FermionSetup', blockValues,
                       observables: HolographicLattices.control_observables.FieldDependentHook):
    """
    Export the fermions to a HDF file. This does __not__ handle the loop correctly yet, but that is because it would be too
    big to use.
    :param filename:
    :param fermions:
    :param setup:
    :param blockValues:
    :param observables:
    :return:
    """
    # This is
    logger = logging.getLogger("fermions")
    with h5py.File(filename, "w") as outfile:
        # Ensure the shape is correct
        logger.info("Writing data to %s", filename)
        fermions = fermions.reshape((*fermions.shape[:2], *setup.grid_sizes))
        fermionData = outfile.create_dataset(u"fermions", data=fermions, dtype=np.complex128)
        for i, b in enumerate(setup.blocks):
            value = blockValues[i]
            fermionData.attrs[b] = value

        fermionData.attrs[u"Number of Fields"] = setup.num_fields
        fermionData.attrs.create(u"periodic", setup.grid_periodic, (setup.dims,))
        fermionData.attrs[u"tolerance"] = setup.tolerance
        fermionData.attrs[u"Number of Background fields"] = setup.nBackgroundFields

        import json
        fermionData.attrs[u"constants"] = json.dumps(setup.constants)
        fermionData.attrs[u"Finite Diff Methods"] = json.dumps(setup.eom_derivative_methods)
        fermionData.attrs[u"Finite Diff Order"] = setup.diff_order

        fermionData.attrs[u"Update Parameter"] = setup.nonlinear_update_step

        bdyVariables, bulkVariables = setup.getBoundaryBulkInput(fermions)
        bdyShapes, bulkShape = setup.boundaryBulkShapes()

        if observables is not None:
            observ = outfile.create_group(u"observables")
            if observables.interiorEquations:
                interior = observ.create_group(u"interior")
                for k, v in observables.interiorEquations.items():
                    interior.create_dataset(k, data=v(**bulkVariables).reshape(bulkShape))

            for i in range(setup.dims):
                for j in range(2):
                    eqns = observables.boundaryEquations[i][j]
                    variables = bdyVariables[i][j]
                    bdyShape = bdyShapes[i]
                    boundaryGroup = None
                    if eqns:
                        if boundaryGroup is None:
                            boundaryGroup = observ.create_group(u"boundaries")
                        for k, v in eqns.items():
                            boundaryGroup.create_dataset(k, data=v(**variables).reshape(bdyShape))
            if observables.globalEquations:
                glob = observ.create_group(u"global")
                allVariables = {**setup.background, setup.field: fermions, **setup.coordinates}
                for k, v in observables.globalEquations.items():
                    glob.create_dataset(k, data=v(**allVariables).reshape(setup.grid_sizes))

# def importBackgroundHDF5
class FermionSolver(HolographicLattices.nonlinear_solver.Solver):
    def __init__(self, setup: 'FermionSetup'):
        super().__init__()
        self.setup = setup
        self.result = None
        self.preSolveHook = lambda *args: None
        self.postSolveHook = lambda *args: None
        self.currentFunction = None

    def sourceMomenta(self):
        setup = self.setup
        sourceMomentum = setup.sourceMomentum
        sourceMomentum = sourceMomentum / np.product(setup.grid_sizes[setup.grid_periodic])
        return sourceMomentum

    def solve(self):
        setup = self.setup
        import copy
        logger = logging.getLogger("fermions")

        setupSource1 = copy.deepcopy(setup)
        setupSource1.constants["source"] = np.array([1, 0], dtype=np.complex128)

        setupSource2 = copy.deepcopy(setup)
        setupSource2.constants["source"] = np.array([0, 1], dtype=np.complex128)

        fermionCoefsSource1, RHSSource1 = loadFermionEquations(setupSource1)
        fermionCoefsSource2, RHSSource2 = loadFermionEquations(setupSource2)

        try:
            obsPath = os.path.join(setup.observablesFolder, "FermionObservables.ini")
            if not os.path.exists(obsPath):
                observable = None
                logger.warning("No control observable file found, assuming averages.")
            else:
                observable = FermionObservables("FermionObservables.ini", setup)
        except IOError as e:
            observable = None
            logger.warning("Error reading observables file.")

        fermionOperators = linearFermionOperators(fermionCoefsSource2, setupSource2)

        fermRHSS1 = fermionRHS(RHSSource1, setupSource1)
        fermRHSS2 = fermionRHS(RHSSource2, setupSource2)

        points = np.array(list(itertools.product(*setup.blockRanges.values())))
        allData = np.zeros((len(points), 4))
        if setup.allFourier:
            logger.info("All fourier components are being used")
            totalSize = 1
            for d, n in zip(setup.grid_periodic, setup.grid_sizes):
                if d:
                    totalSize *= n
            fullResponses = np.zeros((len(points), 4, totalSize), dtype=np.complex128)
        else:
            fullResponses = None
        allData[:, :-1] = points / (setup.constants["mu"])
        sigma2 = np.array([[0, -1.J], [1.J, 0]], dtype=np.complex128)
        nextPercent = 0
        if observable is not None:
            import copy
            observableSetup1 = copy.deepcopy(setupSource1)
            observableSetup2 = copy.deepcopy(setupSource2)
            observableSetup1.max_deriv = 2
            observableSetup2.max_deriv = 2
            observableSetup1.__finalize_init__()
            observableSetup2.__finalize_init__()

        for i, point in enumerate(itertools.product(*setup.blockRanges.values())):

            fullOperator1, RHS1 = getFermionOperator(point, fermionOperators, fermRHSS1, setupSource1)
            fullOperator2, RHS2 = getFermionOperator(point, fermionOperators, fermRHSS2, setupSource2)
            logger.info(f"Starting solve {i}")

            result1 = scipy.sparse.linalg.spsolve(fullOperator1, RHS1).reshape((setup.num_fields, *setup.grid_sizes))
            result2 = scipy.sparse.linalg.spsolve(fullOperator2, RHS2).reshape((setup.num_fields, *setup.grid_sizes))
            if observable is not None:
                if setup.allFourier:
                    Response_1_1_full, Response_1_2_full = observable(result1, point, observableSetup1)
                    Response_2_1_full, Response_2_2_full = observable(result2, point, observableSetup2)
                    fullResponses[i, 0] = Response_1_1_full
                    fullResponses[i, 1] = Response_1_2_full
                    fullResponses[i, 2] = Response_2_1_full
                    fullResponses[i, 3] = Response_2_2_full
                    # with h5py.File(f"testfolder/Fermion_fourier_observable_{point}.h5") as fullObservable:
                    #    Point =  fullObservable.create_dataset("Point", data=point/setup.constants["mu"])
                    #    Greenfunctions= fullObservable.create_dataset("GreenFunction", data=Grs)

                Response_1_1, Response_1_2 = observable(result1, point, observableSetup1, average=True)
                Response_2_1, Response_2_2 = observable(result2, point, observableSetup2, average=True)
            else:
                logger.warning("Observables not found! using average of 0 component")
                Response_1_1 = np.average(result1[0, :, 0])
                Response_1_2 = np.average(result1[2, :, 0])
                Response_2_1 = np.average(result2[0, :, 0])
                Response_2_2 = np.average(result2[2, :, 0])

            R = np.array([[Response_1_1, Response_2_1], [Response_1_2, Response_2_2]])

            # Reverse on 2nd axis == multiply by {{0,1}{1,0}}
            M = R[:, ::-1]
            Gr = - M.dot(sigma2)
            rho = np.trace(np.imag(Gr))
            allData[i, -1] = rho
            logger.debug(f"Evaluated point {point / (setup.constants['mu'])}, rho = {rho}")

            if i * 100 / len(allData) > nextPercent:
                logger.info(f"Currently at {i * 100 / len(allData)} percent")
                while i * 100 / len(allData) > nextPercent:
                    nextPercent += 1
        return allData, fullResponses

class FermionObservables(HolographicLattices.control_observables.FieldDependentHook):
    def __call__(self, fermionFunctions: np.ndarray, blockValues: np.array, setup: 'FermionSetup', average=False):
        logger = logging.getLogger("fermions")
        fieldsDerivatives = HolographicLattices.derivatives.take_derivative_mixed(fermionFunctions, setup)
        boundaryVariables, _ = setup.getBoundaryBulkInput(fieldsDerivatives)
        blockDict = {k: v for k, v in zip(setup.blocks, blockValues)}
        R1 = self.boundaryEquations[1][0]["r1"](**{**boundaryVariables[1][0], **blockDict})
        R2 = self.boundaryEquations[1][0]["r2"](**{**boundaryVariables[1][0], **blockDict})
        if average:
            return np.average(R1), np.average(R2)
        else:
            return R1, R2

class FermionSetup(HolographicLattices.Options.Options.Setup):
    def __init__(self, filename, args=None):
        logger = logging.getLogger("fermions")
        super().__init__(filename, args)
        if filename is not None:
            try:
                import configparser
                import json

                with open(filename, "r") as infile:
                    self.fileLines = infile.readlines()
                    self.fileLines = "\n".join([x.strip() for x in self.fileLines if len(x.strip()) > 0])

                config = configparser.ConfigParser()
                config.read(filename)

                fermions = config["Fermions"]
                self.nBackgroundFields = fermions.getint("N_BACKGROUND_FIELDS", 4)
                self.backgroundFile = fermions.get("BACKGROUND_FILE", "FermionBackgrounds.csv")
                self.backgroundName = fermions.get("BACKGROUND_NAME", "f")
                self.reinterpolate = fermions.getboolean("REINTERPOLATE", False)
                self.constants["sourceMomentum"] = json.loads(fermions.get("SOURCE_MOMENTUM", "[0]"))

                self.background = {self.backgroundName: self.__loadBackground__(self.backgroundFile)}
                self.allFourier = fermions.getboolean("ALL_FOURIER", False)

                T = self.constants["t"]
                mu = 2 * (-4 * np.pi * T + np.sqrt(3 + 16 * np.pi ** 2 * T ** 2))
                self.constants["mu"] = mu
                self.constants["mu1"] = mu
                self.constants["p0"] = mu * self.constants["plattice"] / self.constants["nperiods"]

                self.blocks = json.loads(fermions.get("BLOCKS", '["ω", "kx"]'))
                self.blockRanges = {}
                for block in self.blocks:
                    self.blockRanges[block] = np.linspace(*eval(fermions.get(block, "[1,1,1]")))
                    # This multiplies by the chemical potential to be able to evaluate mu in terms of chemical potential
                    self.blockRanges[block] = self.constants["mu"] * self.blockRanges[block]

            except IOError as e:
                logger.error(f"Error reading file: {filename}. Assuming default options:{e}")

        if args is not None:
            # Command line arguments are parsed if they are given. These override anything set in the settings file.
            import argparse
            parser = argparse.ArgumentParser(add_help=False)

            # Add options here
            parsed, _ = parser.parse_known_args(args)

            # Add saving of parsing here
        self.__finalize_init__()

    def __loadBackground__(self, filename):
        """
        Loads the background in from a file. Since this is highly nontrivial, it assumes the following structure:
        nx * ny * nz ... for num_fields fields, but it currently does not do e.g. interpolation or evaluation, it is just
        reading it in.
        :return:
        """
        logger = logging.getLogger("fermions")
        #    raise ValueError("Need to specify absolute path for this. To be fixed")
        with h5py.File(filename, "r") as inFile:
            background = inFile["/field"]
            import json
            for k, v in json.loads(background.attrs["constants"]).items():
                self.constants[k] = v

            if (np.abs(self.constants["m"]) < 1e-6 or np.abs(self.constants["m"] - 1) < 1e-6) and self.reinterpolate:
                logger.info("Mass is 0 or 1, using mass = 0 transform")
                oldData = background[:, 0, :].reshape((background.shape[0], *self.grid_sizes))
                oldGrid = self.grids["y"]
                import invertz

                newData = invertz.invertz_m0(oldData, oldGrid)
                derivNewData = HolographicLattices.derivatives.take_deriv_bg(newData, self)

                return derivNewData
            elif np.abs(self.constants["m"] - (1 / 4)) < 1e-6 and self.reinterpolate:
                logger.info("Mass is 1/4, using m = 1/4 transform")
                oldData = background[:, 0, :].reshape((background.shape[0], *self.grid_sizes))
                oldGrid = self.grids["y"]
                import invertz
                newData = invertz.invertz_m14(oldData, oldGrid)
                derivNewData = HolographicLattices.derivatives.take_deriv_bg(newData, self)
                return derivNewData

            else:
                return background[:]

    def __finalize_init__(self):
        # This method does the usual post-initialization, but it also takes the derivatives of the background field, so
        # that they can be used easily in further evaluations.
        super().__finalize_init__()

    def getVariableNames(self):
        return [*self.grid_names, self.field, self.backgroundName, *self.blocks]

    def getBoundaryBulkInput(self, fermionFields=None, *args):
        """ This gets the boundary and bulk input for a given problem, which needs to be
        overridden for the Fermion setup"""
        bulkVariables = {**self.coordinates}
        bulkVariables[self.backgroundName] = self.background[self.backgroundName].reshape(
            *self.background[self.backgroundName].shape[:2], self.gridVolume)

        for ax in self.grid_names:
            bulkVariables[ax] = bulkVariables[ax][self.bulkIndices]
        bulkVariables[self.backgroundName] = bulkVariables[self.backgroundName][:, :, self.bulkIndices]
        if fermionFields is not None:
            bulkVariables[self.field] = fermionFields.reshape((*fermionFields.shape[:2], self.gridVolume))[:, :,
                                        self.bulkIndices]

        retBoundary = [[None, None] for _ in range(self.dims)]
        for i in range(self.dims):
            if not self.grid_periodic[i]:
                for j in range(2):
                    idx = self.boundaryIndices[i][j]
                    boundaryVariables = {**self.background, **self.coordinates}
                    boundaryVariables[self.backgroundName] = self.background[self.backgroundName].reshape(
                        *self.background[self.backgroundName].shape[:2], self.gridVolume)
                    for ax in self.grid_names:
                        boundaryVariables[ax] = boundaryVariables[ax][idx]
                    boundaryVariables[self.backgroundName] = boundaryVariables[self.backgroundName][:, :, idx]

                    if fermionFields is not None:
                        boundaryVariables[self.field] = fermionFields.reshape(
                            (*fermionFields.shape[:2], self.gridVolume))[:,
                                                        :, idx]
                    retBoundary[i][j] = boundaryVariables

        return retBoundary, bulkVariables

def main():
    timeStart = time.time()
    import argparse
    parser = argparse.ArgumentParser()

    parser.add_argument("--setup", type=str, help="Setup file to use")
    parser.add_argument("--outfile", type=str, help="Where to output as hdf5 file")
    parser.add_argument("--logfile", type=str, help="Where to log to")
    parser.add_argument("--log", type=str, default="INFO", help="Logging level to use. Pick one of: DEBUG, INFO, "
                                                                "WARN, ERROR")
    parsed, other_args = parser.parse_known_args()
    setupFile = parsed.setup
    print("Using setup file", setupFile)
    loggingLevel = getattr(logging, parsed.log.upper())
    logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", filename=parsed.logfile,
                        level=loggingLevel)
    logger = logging.getLogger("fermions")
    logger.info(f"Started running at {timeStart}")

    setup = FermionSetup(setupFile)

    solver = FermionSolver(setup)

    logger.info("Loaded fermions successfully")

    if parsed.outfile is not None:
        f = h5py.File(parsed.outfile, "w")
    else:
        f = h5py.File(setup.outfile, "w")

    logger.info(f"Saving to {f.filename}")
    result, resultFullMat = solver.solve()
    import json
    sF = f.create_dataset("SpectralFunction", data=result)
    sF.attrs["shape"] = np.array([len(setup.blockRanges[block]) for block in setup.blocks])
    sF.attrs["constants"] = json.dumps(setup.constants)
    sF.attrs["setup"] = setup.fileLines
    logger.info("Created spectral function")
    if resultFullMat is not None:
        logger.info("Also outputting full matrix")
        resp = f.create_dataset("FullResponses", data=resultFullMat, dtype=np.complex128)
        resp.attrs["shape"] = np.array([len(setup.blockRanges[block]) for block in setup.blocks])
        resp.attrs["constants"] = json.dumps(setup.constants)
        resp.attrs["setup"] = setup.fileLines
    f.close()
    timeEnd = time.time()
    logger.info(f"Time taken: {timeEnd - timeStart}")

if __name__ == "__main__":
    main()


# -*- coding: utf-8 -*-
"""
Created on Mon Dec 30 14:37:43 2019
dimension reduction pca
@author: Beytu
"""

#1.import libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

#2. data preprocessing

#2. import data
veriler = pd.read_csv('Wine.csv')

X=veriler.iloc[:,0:13].values
Y=veriler.iloc[:,13].values

#split the data train and test
from sklearn.model_selection import train_test_split
x_train, x_test,y_train,y_test = train_test_split(X,Y,test_size=0.2, random_state=0)

#data normalization
from sklearn.preprocessing import StandardScaler

sc = StandardScaler()
X_train = sc.fit_transform(x_train)
X_test = sc.transform(x_test)

#PCA
from sklearn.decomposition import PCA
pca=PCA(n_components=2)
X_train2=pca.fit_transform(X_train)
X_test2=pca.transform(X_test)

#before pca
from sklearn.linear_model import LogisticRegression
classifier=LogisticRegression(random_state=0)
classifier.fit(X_train,y_train)

#after pca
classifier2=LogisticRegression(random_state=0)
classifier2.fit(X_train2,y_train)

#prediction
y_pred=classifier.predict(X_test)
y_pred2=classifier2.predict(X_test2)

from sklearn.metrics import confusion_matrix
cm=confusion_matrix(y_test,y_pred)
print('real/without pca')
print(cm)

cm1=confusion_matrix(y_test,y_pred2)
print('real/with pca')
print(cm1)

cm2=confusion_matrix(y_pred,y_pred2)
print('without pca and with pca')
print(cm2)

#lda linear discriminat analysis
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
lda=LDA(n_components=2)
X_train_lda=lda.fit_transform(X_train,y_train)
X_test_lda=lda.transform(X_test)

#after lda
classifier_lda=LogisticRegression(random_state=0)
classifier_lda.fit(X_train_lda,y_train)

#predict
y_pred_lda=classifier_lda.predict(X_test_lda)

cm3=confusion_matrix(y_test,y_pred_lda)
print('lda/real')
print(cm3)


import os
import subprocess

from aws_cdk import (
    Duration,
    Stack,
    aws_lambda as _lambda,
    aws_dynamodb as dynamodb,
    aws_apigateway as api_gw
)
from constructs import Construct

class PyconIt2023Stack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        layer = self.create_dependencies_layer('pycon-it-2023')

        table = dynamodb.Table(
            self, 'PullRequests',
            partition_key=dynamodb.Attribute(
                name='timestamp',
                type=dynamodb.AttributeType.STRING
            )
        )

        integration_responses = [
            {
                'statusCode': '200'
            },
            {
                'statusCode': '500',
                'selectionPattern': '^Unexpected error$',
            }
        ]

        method_responses = [
            {
                'statusCode': '200',
                'responseModels': {
                    'application/json': api_gw.Model.EMPTY_MODEL,
                },
            },
            {
                'statusCode': '500',
                'responseModels': {
                    'application/json': api_gw.Model.EMPTY_MODEL,
                },
            }
        ]

        gateway = self.create_api_gateway('pycon-it-2023')

        resource = gateway.root.add_resource('pull-requests')

        functions = (
            'get',
            'post'
        )

        for function_name in functions:
            lambda_function = self.create_lambda_function(
                function_name,
                layer,
                table
            )

            resource.add_method(
                function_name.capitalize(),
                api_key_required=True,
                integration=api_gw.LambdaIntegration(
                    handler=lambda_function,
                    proxy=False,
                    integration_responses=integration_responses,
                ),
                method_responses=method_responses
            )

            if function_name == 'get':
                table.grant_read_data(lambda_function)
            elif function_name == 'post':
                table.grant_write_data(lambda_function)

    def create_api_gateway(
        self,
        gw_name: str,
    ) -> api_gw.RestApi:
        gateway = api_gw.RestApi(
            self, f'{gw_name}Api',
            rest_api_name=gw_name,
        )

        plan = gateway.add_usage_plan(
            f'{gw_name}UsagePlan',
            name=gw_name,
            throttle=api_gw.ThrottleSettings(
                rate_limit=100,
                burst_limit=200
            )
        )

        key = gateway.add_api_key(f'{gw_name}Key')
        plan.add_api_key(key)
        plan.add_api_stage(
            stage=gateway.deployment_stage
        )

        return gateway

    def create_lambda_function(
        self,
        function_name: str,
        lambda_layer: _lambda.LayerVersion,
        dynamo_table: dynamodb.Table
    ) -> _lambda.Function:
        lambda_function = _lambda.Function(
            self,
            f'{function_name}-pycon-it-lambda',
            runtime=_lambda.Runtime.PYTHON_3_9,
            code=_lambda.Code.from_asset('code'),
            handler=f'{function_name}.lambda_handler',
            timeout=Duration.seconds(30),
            memory_size=128,
            layers=[
                lambda_layer
            ],
            environment={
                'DYNAMO_TABLE': dynamo_table.table_name
            }
        )

        return lambda_function

    def create_dependencies_layer(self, function_name: str) -> _lambda.LayerVersion:
        requirements_file = 'code/requirements.txt'
        output_dir = '.lambda_dependencies/' + function_name
        # Install requirements for layer in the output_dir
        if not os.environ.get('SKIP_PIP'):
        # Note: Pip will create the output dir if it does not exist
            subprocess.check_call(
            f'pip install -r {requirements_file} -t {output_dir}/python'.split()
            )
        return _lambda.LayerVersion(
            self,
            function_name + '-dependencies',
            code=_lambda.Code.from_asset(output_dir)
        )


import snowflake.snowpark.functions as F
from snowflake.snowpark.functions import lit
import datetime

def model(dbt, session):

    df = dbt.ref("demand_forecast_training_base")
    orders_df = dbt.ref("fct_orders")

    # Get earliest date
    min_date = df.select(F.dateadd("year", F.lit(1), F.min("date"))).collect()[0][0]

    # Filter out the first year in the data
    df = df.filter(F.col("date") >= min_date)

    # Join in third party data
    df_locations = orders_df.select("location_id", "latitude", "longitude").distinct()
    df = df.join(df_locations, "location_id")

    df_holidays = orders_df.select("date", "primary_city", "holiday_flag").distinct()
    df = df.join(df_holidays, ["date", "primary_city"])

    # Encode
    df = df.with_column("shift", F.iff(F.col("shift") == "AM", 1, 0))

    # Split into training and testing
    max_date = df.select(F.max("date")).collect()[0][0]
    df = df.with_column("split_type",
                        F.iff(F.col("date") <= (max_date - datetime.timedelta(days=30)),
                                F.lit("train"),
                                F.lit("test")))

    # Drop ID columns
    df = df.drop("date",
            "primary_city",
            "location_id",
            "menu_type",
            "menu_item_id",)

    return df


"""https://sagebionetworks.jira.com/browse/WORKFLOWS-390
This workflow was created to run the `nf-core/rnaseq` pipeline for GRCh38 RNASeq data.
"""
import asyncio
import boto3

from synapseclient import Synapse
from dataclasses import dataclass
from pathlib import Path

from orca.services.nextflowtower import NextflowTowerOps
from orca.services.nextflowtower.models import LaunchInfo

session = boto3.Session(profile_name="nextflow-prod")
s3 = session.client("s3")

async def main():
    ops = NextflowTowerOps()
    datasets = generate_datasets()
    runs = [run_workflows(ops, dataset) for dataset in datasets]
    statuses = await asyncio.gather(*runs)
    print(statuses)

@dataclass
class Dataset:
    id: str
    """The synapse id for the samplesheet."""

    samplesheet: str
    """The name of the samplesheet to run."""

    synapse_id_for_output: str
    """The synapse id for the output folder, this is where the output will be uploaded to."""

    run_number: int
    """The number for the run, this is used to generate the run_name for the workflow."""

    output_number: int
    """The number for the output, in some cases this is the same as the run_number,
    but in other cases it isn't because the pipeline has been re-ran."""

    bucket_name: str
    """The name of the bucket to stage the samplesheet in."""

    staging_key: str
    """The key in the S3 bucket where this workflow is going to run."""

    @property
    def samplesheet_location(self) -> str:
        """The location where the unstaged samplesheet is located."""
        return f"{self.samplesheet_location_prefix}{self.samplesheet}"

    @property
    def samplesheet_to_stage_key(self) -> str:
        """The key in the S3 bucket where the samplesheet is going to be staged."""
        return f"{self.staging_key}to_stage/{self.samplesheet}"

    @property
    def staged_samplesheet_location(self) -> str:
        """The S3 uri where the samplesheet is staged."""
        return f"{self.staging_location}synstage_{self.id}/{self.samplesheet}"

    @property
    def staging_location(self) -> str:
        """The S3 uri where the workflow is going to be run."""
        return f"s3://{self.bucket_name}/{self.staging_key}"

    @property
    def samplesheet_location_prefix(self) -> str:
        """The S3 uri where the unstaged samplesheet is located."""
        return f"s3://{self.bucket_name}/{self.staging_key}to_stage/"

    @property
    def output_directory(self) -> str:
        """The S3 uri where the output is going to be uploaded to. The is used as the
        input for the synindex workflow."""
        return f"s3://{self.bucket_name}/outputs/rnaseq_GRCh38_{self.id}_{self.output_number}/"

    @property
    def synstage_run_name(self) -> str:
        """The name of the synstage run."""
        return f"synstage_{self.id}"

    @property
    def rnaseq_run_name(self) -> str:
        """The name of the rnaseq run."""
        return f"rnaseq_GRCh38_{self.id}_{self.run_number}"

    @property
    def synindex_run_name(self) -> str:
        """The name of the synindex run."""
        return f"synindex_{self.id}_{self.run_number}"

def generate_datasets() -> list[Dataset]:
    """Generate list of datasets.

    Source: https://sagebionetworks.jira.com/browse/WORKFLOWS-538

    This is expecting that the SampleSheet to be run exists in the `prefix` location.
    """
    return [
        Dataset(
            id="syn52120378",
            samplesheet="syn51199007_RNA_Seq_SampleSheet.csv",
            staging_key="samplesheets/Reprocess/WORKFLOWS-390/",
            bucket_name="ntap-add5-project-tower-bucket",
            synapse_id_for_output="syn53072020",
            run_number=2,
            output_number=1,
        )
    ]

def stage_samplesheet(syn: Synapse, dataset: Dataset):
    """Download the samplesheet from synapse and upload it to S3 in the location where synstage
    is going to grab the file.

    :param syn: The logged in synapse instance.
    :param dataset: The dataset to stage the samplesheet for.
    """
    samplesheet_file = syn.get(dataset.id)
    samplesheet_file_path = samplesheet_file.path

    s3.upload_file(
        samplesheet_file_path, dataset.bucket_name, dataset.samplesheet_to_stage_key
    )

def prepare_synstage_info(dataset: Dataset) -> LaunchInfo:
    """Generate LaunchInfo for nf-synstage."""
    return LaunchInfo(
        run_name=dataset.synstage_run_name,
        pipeline="Sage-Bionetworks-Workflows/nf-synstage",
        revision="disable_wave",
        profiles=["sage"],
        params={
            "input": dataset.samplesheet_location,
            "outdir": dataset.staging_location,
        },
        pre_run_script="NXF_VER=22.10.4",
        workspace_secrets=["SYNAPSE_AUTH_TOKEN"],
        nextflow_config="wave.enabled=false",
    )

def prepare_rnaseq_launch_info(dataset: Dataset) -> LaunchInfo:
    """Generate LaunchInfo for nf-core/rnaseq workflow run."""
    return LaunchInfo(
        run_name=dataset.rnaseq_run_name,
        pipeline="nf-core/rnaseq",
        revision="3.11.2",
        profiles=["sage"],
        params={
            "input": dataset.staged_samplesheet_location,
            "outdir": dataset.output_directory,
            "gencode": True,
            "fasta": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Sequence/WholeGenomeFasta/genome.fa",
            "gtf": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.gtf",
            "gene_bed": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.bed",
            "star_index": "s3://sage-igenomes/igenomes/Homo_sapiens/NCBI/GRCh38/Sequence/STARIndex/",
            # TODO: These could not be found in `s3://sage-igenomes/igenomes/Homo_sapiens/`
            # "transcript_fasta": "",
            # "salmon_index": "",
            # "rsem_index": "",
        },
        pre_run_script="NXF_VER=23.10.0",
        nextflow_config=Path("local/ntap/nextflow-rnaseq.config").read_text(),
    )

def prepare_synindex_launch_info(dataset: Dataset) -> LaunchInfo:
    """Generate LaunchInfo for nf-synindex workflow run."""
    return LaunchInfo(
        run_name=dataset.synindex_run_name,
        pipeline="Sage-Bionetworks-Workflows/nf-synindex",
        revision="disable_wave",
        profiles=["sage"],
        params={
            "s3_prefix": dataset.output_directory,
            "parent_id": dataset.synapse_id_for_output,
        },
        pre_run_script="NXF_VER=22.10.4",
        workspace_secrets=["SYNAPSE_AUTH_TOKEN"],
        nextflow_config="wave.enabled=false",
    )

async def run_workflows(ops: NextflowTowerOps, dataset: Dataset):
    syn = Synapse()
    syn.login()

    stage_samplesheet(syn, dataset)

    synstage_info = prepare_synstage_info(dataset)
    synstage_run_id = ops.launch_workflow(synstage_info, "spot")
    status = await ops.monitor_workflow(run_id=synstage_run_id, wait_time=60 * 2)
    print(status)

    scrnaseq_info = prepare_rnaseq_launch_info(dataset)
    scrnaseq_run_id = ops.launch_workflow(scrnaseq_info, "spot")
    status = await ops.monitor_workflow(run_id=scrnaseq_run_id, wait_time=60 * 2)
    print(status)

    synindex_info = prepare_synindex_launch_info(dataset)
    synindex_run_id = ops.launch_workflow(synindex_info, "spot")
    status = await ops.monitor_workflow(run_id=synindex_run_id, wait_time=60 * 2)
    print(status)

if __name__ == "__main__":
    asyncio.run(main())


import tkinter as tk
from tkinter import messagebox, ttk
import sqlite3

# Database filename
DATABASE = 'quiz_game.db'

class TopicDialog(tk.Toplevel):
    def __init__(self, parent, title, topic_id=None):
        super().__init__(parent)
        self.title(title)
        self.topic_id = topic_id
        self.result = None

        tk.Label(self, text="Topic Name:").grid(row=0, column=0, padx=10, pady=10)
        self.topic_name_var = tk.StringVar()
        self.topic_entry = tk.Entry(self, textvariable=self.topic_name_var)
        self.topic_entry.grid(row=0, column=1, padx=10, pady=10)

        save_button = tk.Button(self, text="Save", command=self.on_save)
        save_button.grid(row=1, column=1, sticky=tk.W, padx=10, pady=10)

        if topic_id:
            # Load the topic name from the database
            topic_data = execute_db_query("SELECT topic_name FROM Topics WHERE topic_id=?", (topic_id,), fetch=True)
            if topic_data:
                self.topic_name_var.set(topic_data[0][0])

        self.topic_entry.focus_set()

    def on_save(self):
        topic_name = self.topic_name_var.get().strip()
        if topic_name:
            if self.topic_id:
                # Update existing topic
                execute_db_query("UPDATE Topics SET topic_name=? WHERE topic_id=?", (topic_name, self.topic_id))
            else:
                # Insert new topic
                execute_db_query("INSERT INTO Topics (topic_name) VALUES (?)", (topic_name,))
            self.result = topic_name
            self.destroy()
        else:
            messagebox.showwarning("Warning", "The topic name cannot be empty.", parent=self)

class QuestionDialog(tk.Toplevel):
    def __init__(self, parent, title, topic_id, question_id=None):
        super().__init__(parent)
        self.title(title)
        self.question_id = question_id
        self.topic_id = topic_id
        self.result = None

        tk.Label(self, text="Question Text:").grid(row=0, column=0, padx=10, pady=10)
        self.question_text_var = tk.StringVar()
        self.question_entry = tk.Entry(self, textvariable=self.question_text_var)
        self.question_entry.grid(row=0, column=1, padx=10, pady=10)

        save_button = tk.Button(self, text="Save", command=self.on_save)
        save_button.grid(row=1, column=1, sticky=tk.W, padx=10, pady=10)

        if question_id:
            # Load the question text from the database
            question_data = execute_db_query("SELECT question_text FROM Questions WHERE question_id=?", (question_id,), fetch=True)
            if question_data:
                self.question_text_var.set(question_data[0][0])

        self.question_entry.focus_set()

    def on_save(self):
        question_text = self.question_text_var.get().strip()
        if question_text:
            if self.question_id:
                # Update existing question
                execute_db_query("UPDATE Questions SET question_text=? WHERE question_id=?", (question_text, self.question_id))
            else:
                # Insert new question
                execute_db_query("INSERT INTO Questions (topic_id, question_text) VALUES (?, ?)", (self.topic_id, question_text))
            self.result = question_text
            self.destroy()
        else:
            messagebox.showwarning("Warning", "The question text cannot be empty.", parent=self)

class AnswerDialog(tk.Toplevel):
    def __init__(self, parent, title, question_id, answer_id=None):
        super().__init__(parent)
        self.title(title)
        self.answer_id = answer_id
        self.question_id = question_id
        self.result = None

        tk.Label(self, text="Answer Text:").grid(row=0, column=0, padx=10, pady=10)
        self.answer_text_var = tk.StringVar()
        self.answer_entry = tk.Entry(self, textvariable=self.answer_text_var)
        self.answer_entry.grid(row=0, column=1, padx=10, pady=10)

        self.is_correct_var = tk.BooleanVar()
        self.is_correct_check = tk.Checkbutton(self, text="Correct answer?", variable=self.is_correct_var)
        self.is_correct_check.grid(row=1, column=1, sticky=tk.W, padx=10, pady=10)

        save_button = tk.Button(self, text="Save", command=self.on_save)
        save_button.grid(row=2, column=1, sticky=tk.W, padx=10, pady=10)

        if answer_id:
            # Load the answer text and correctness from the database
            answer_data = execute_db_query("SELECT answer_text, is_correct FROM Answers WHERE answer_id=?", (answer_id,), fetch=True)
            if answer_data:
                self.answer_text_var.set(answer_data[0][0])
                self.is_correct_var.set(answer_data[0][1])

        self.answer_entry.focus_set()

    def on_save(self):
        answer_text = self.answer_text_var.get().strip()
        is_correct = self.is_correct_var.get()
        if answer_text:
            if self.answer_id:
                # Update existing answer
                execute_db_query("UPDATE Answers SET answer_text=?, is_correct=? WHERE answer_id=?", (answer_text, is_correct, self.answer_id))
            else:
                # Insert new answer
                execute_db_query("INSERT INTO Answers (question_id, answer_text, is_correct) VALUES (?, ?, ?)", (self.question_id, answer_text, is_correct))
            self.result = (answer_text, is_correct)
            self.destroy()
        else:
            messagebox.showwarning("Warning", "The answer text cannot be empty.", parent=self)

def execute_db_query(query, parameters=(), fetch=False):
    with sqlite3.connect(DATABASE) as conn:
        cursor = conn.cursor()
        cursor.execute(query, parameters)
        if fetch:
            return cursor.fetchall()
        conn.commit()

def load_topics_from_db():
    return execute_db_query("SELECT * FROM Topics", fetch=True)

def load_questions_from_db(topic_id):
    return execute_db_query("SELECT * FROM Questions WHERE topic_id=?", (topic_id,), fetch=True)

def load_answers_from_db(question_id):
    return execute_db_query("SELECT * FROM Answers WHERE question_id=?", (question_id,), fetch=True)

def edit_questions_window(main_root):
    # Function to refresh the tree view
    def refresh_tree():
        tree.delete(*tree.get_children())  # Clear the tree view
        for topic in load_topics_from_db():
            topic_id = topic[0]
            topic_title = topic[1]
            topic_node = tree.insert('', 'end', iid=f"topic_{topic_id}", values=(topic_id, topic_title, 'Topic'))

            for question in load_questions_from_db(topic_id):
                question_id = question[0]
                question_text = question[2]  # Adjust the index as needed
                # One indent and a ↳ for questions
                question_node = tree.insert(topic_node, 'end', iid=f"question_{question_id}", values=(question_id, '    ' + '↳ ' + question_text, 'Question'))

                for answer in load_answers_from_db(question_id):
                    answer_id = answer[0]
                    answer_text = answer[2]  # Adjust the index as needed
                    is_correct = answer[3]  # Adjust the index as needed
                    correct_text = "Correct" if is_correct else "Incorrect"
                    # Two indents and a ↳ for answers
                    tree.insert(question_node, 'end', iid=f"answer_{answer_id}", values=(answer_id, '       ' + '↳ ' + answer_text + ' (' + correct_text + ')', 'Answer'))

    # Create a top-level window
    edit_window = tk.Toplevel(main_root)
    edit_window.title('Edit Questions')
    edit_window.state('zoomed')  # Fullscreen mode

    # Treeview widget
    tree = ttk.Treeview(edit_window, columns=('ID', 'Description', 'Type'), show='headings')
    tree.pack(expand=True, fill='both')
    # Define ID
    # When setting up the Treeview columns, set the ID column width and minwidth to 0
    tree.column('ID', width=0, minwidth=0, stretch=tk.NO)
    tree.heading('ID', text='ID', anchor=tk.CENTER)  # You can keep the heading configuration if needed for sorting or other operations

    # Define columns
    tree.column('Description', anchor=tk.W, width=800)
    tree.column('Type', anchor=tk.W, width=120)

    # Create headings
    tree.heading('Description', text='Description', anchor=tk.W)
    tree.heading('Type', text='Type', anchor=tk.W)

    # Buttons for managing data
    btn_frame = tk.Frame(edit_window)
    btn_frame.pack(fill='x')

    add_btn = tk.Button(btn_frame, text="Add")
    edit_btn = tk.Button(btn_frame, text="Edit")
    delete_btn = tk.Button(btn_frame, text="Delete")

    add_btn.pack(side=tk.LEFT, padx=10, pady=10)
    edit_btn.pack(side=tk.LEFT, padx=10, pady=10)
    delete_btn.pack(side=tk.LEFT, padx=10, pady=10)

    def get_selected_topic_or_question(tree):
    # Helper function to return the selected topic or question and its type
        selected_item = tree.selection()
        if selected_item:
            item_id, _, item_type = tree.item(selected_item, 'values')
            return item_id, item_type
        return None, None

    def add_item():
        selected_item = tree.selection()
        item_id, item_type = None, None

        # If there's a selected item, get its ID and type
        if selected_item:
            item_id, _, item_type = tree.item(selected_item, 'values')

        if item_type == 'Question':
            # Adding an answer to the selected question
            dialog = AnswerDialog(edit_window, "Add Answer", question_id=item_id)
        elif item_type == 'Topic':
            # Adding a question to the selected topic
            dialog = QuestionDialog(edit_window, "Add Question", topic_id=item_id)
        else:
            # If no item is selected or the item is not a question, default to adding a new topic
            dialog = TopicDialog(edit_window, "Add Topic")

        edit_window.wait_window(dialog)

        # After closing the dialog, refresh the tree if an item was added
        if dialog.result:
            refresh_tree()

    # You need to implement the edit_item function
    def edit_item():
        selected_item = tree.selection()
        if selected_item:
            item_id, _, item_type = tree.item(selected_item, 'values')
            if item_type == 'Topic':
                dialog = TopicDialog(edit_window, "Edit Topic", topic_id=item_id)
                edit_window.wait_window(dialog)
                if dialog.result:
                    refresh_tree()
            elif item_type == 'Question':
                # Retrieve the topic_id by finding the parent item in the tree
                parent_item = tree.parent(selected_item)
                topic_id = tree.item(parent_item, 'values')[0]
                dialog = QuestionDialog(edit_window, "Edit Question", topic_id, question_id=item_id)
                edit_window.wait_window(dialog)
                if dialog.result:
                    refresh_tree()
            elif item_type == 'Answer':
                # Retrieve the question_id by finding the parent item in the tree
                parent_item = tree.parent(selected_item)
                question_id = tree.item(parent_item, 'values')[0]
                dialog = AnswerDialog(edit_window, "Edit Answer", question_id, answer_id=item_id)
                edit_window.wait_window(dialog)
                if dialog.result:
                    refresh_tree()
            else:
                messagebox.showwarning("Warning", "Please select a valid item to edit.")
        else:
            messagebox.showwarning("Warning", "Please select an item to edit.")

    def delete_item():
        selected_item = tree.selection()
        if selected_item:
            item_id, item_description, item_type = tree.item(selected_item, 'values')
            response = messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete this {item_type.lower()}:\n{item_description}?", parent=edit_window)
            if response:
                try:
                    if item_type == 'Topic':
                        execute_db_query("DELETE FROM Topics WHERE topic_id=?", (item_id,))
                    elif item_type == 'Question':
                        execute_db_query("DELETE FROM Questions WHERE question_id=?", (item_id,))
                    elif item_type == 'Answer':
                        execute_db_query("DELETE FROM Answers WHERE answer_id=?", (item_id,))
                    else:
                        messagebox.showerror("Error", "Unknown item type. Cannot delete.", parent=edit_window)
                        return
                    refresh_tree()  # Refresh the interface to reflect the deletion
                except sqlite3.IntegrityError as e:
                    messagebox.showerror("Error", "This item cannot be deleted because it is in use.", parent=edit_window)
        else:
            messagebox.showwarning("Warning", "Please select an item to delete.", parent=edit_window)

    # Bind buttons to event handlers
    add_btn.config(command=add_item)
    edit_btn.config(command=edit_item)
    delete_btn.config(command=delete_item)

    # Populate the tree with data
    refresh_tree()

    return edit_window


from PyQt5 import QtCore
from ui.test.record.Util import TFile
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem, QAbstractItemView
from PyQt5.QtGui import QDropEvent

class TableWidgetView(QTableWidget):
    console_signal = QtCore.pyqtSignal(str)

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

        self.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)
        self.setRowCount(100)
        self.setColumnCount(7)
        self.setAcceptDrops(True)  # 开启接受拖入
        self.setDragEnabled(True)  # 开启拖拽
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setDragDropMode(QAbstractItemView.InternalMove)
        self.setHorizontalHeaderLabels(['事件名称', "类型",'事件内容', "对比值",'时间秒', '次数',"异常"])

        self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # 自适应表格宽度
        self.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)  # 自适应表格高度

        self.horizontalHeader().setSectionResizeMode(0, QHeaderView.Interactive)
        self.horizontalHeader().setSectionResizeMode(1, QHeaderView.Interactive)
        self.horizontalHeader().setSectionResizeMode(6, QHeaderView.Interactive)

        self.setContextMenuPolicy(Qt.CustomContextMenu)  # 允许右键产生子菜单
        self.customContextMenuRequested.connect(self.tableWidget_menu)  # 右键菜单

        try:
            item = self.mainwindow.FileTreeView.currentItem().text()  # 获取文件列表选择文件名称
        except:
            return
        json_path = TFile.get_record_path()+item
        json_list = TFile.get_json_list(json_path)
        if not json_list:
            self.console_signal.emit('文件无数据:%s' % item)
            return

        for x , value in enumerate (json_list.values()):
            self.table_write(x, 0, value['name'])
            self.table_write(x, 1, value['types'])
            self.table_write(x, 2, value['content'])
            self.table_write(x, 3, value['value'])
            self.table_write(x, 4, value['sleep'])
            self.table_write(x, 5, value['num'])
            self.table_write(x, 6, value['exception'])

    def table_write(self,x, y, items):
        if y == 1 or y == 6:
            item_list = items.split(',')
            self.set_ComboBox_value(item_list,x,y)
        else:
            newItem = QTableWidgetItem(str(items))
            newItem.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            self.setItem(x, y, newItem)
        self.selectColumn(1)
        self.set_scrollToItem(x)

    def set_scrollToItem(self,rows):
        """ 自动滚动 """
        self.scrollToItem(self.selectRow(rows),QAbstractItemView.PositionAtCenter)

    def keyPressEvent(self, event):
        if event.key() == 16777223:  # 快捷键delete键
            self.delete_item()

    def tableWidget_menu(self, pos):
        menu = QMenu()
        item2 = menu.addAction(u"Top")
        item3 = menu.addAction(u"Bottom")
        item1 = menu.addAction(u"Delete")
        item4 = menu.addAction(u"Delete all")
        try:
            action = menu.exec_(self.mapToGlobal(pos))
        except:
            return
        if action == item1:
            self.delete_item()
        if action == item2:
            self.top_item()
        if action == item3:
            self.bottom_item()
        if action == item4:
            self.delete_all_item()

    def bottom_item(self):
        """ 置底 """
        for i in self.selectedItems():
            i.row(), i.column(), i.text()
        type_list,exception_list = self.get_ComboBox_value(i.row())
        count = self.get_row_count()
        self.set_selected_to_row(count)
        self.set_ComboBox_value(type_list,count,1)
        self.set_ComboBox_value(exception_list,count,6)
        self.delete_item()
        self.scrollToItem(self.selectRow(j - 1), QAbstractItemView.PositionAtCenter)  # 自动滚动
        TFile.set_json_record(self.mainwindow.FileTreeView.currentItem().text(), self.get_all_item_dic(),is_clean=True)

    def top_item(self):
        """ 置顶 """
        for i in self.selectedItems():
            i.row(), i.column(), i.text()
        type_list,exception_list = self.get_ComboBox_value(i.row())
        self.insertRow(0)  # 插入到第一行
        self.set_selected_to_row(0)
        self.set_ComboBox_value(type_list,0,1)
        self.set_ComboBox_value(exception_list,0,6)
        self.delete_item()
        self.scrollToItem(self.selectRow(0), QAbstractItemView.PositionAtCenter)
        TFile.set_json_record(self.mainwindow.FileTreeView.currentItem().text(), self.get_all_item_dic(),is_clean=True)

    def get_row_count(self):
        row = self.rowCount()
        for count in range(0, row):
            items = self.item(count, 0)
            if items is None:
                return count

    def set_selected_to_row(self,row):
        """ 设置选中行到指定行 """
        y = 0
        for c, data in enumerate(self.get_selectedItems()):
            data.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
            if c == 1 or c == 6:   #
                y = y + 1
            self.setItem(row, y, data)
            y = y + 1

    def get_selectedItems(self):
        """ 获取选中的数据 """
        selectedItemsList = []
        contents = self.selectedItems()
        for c, data in enumerate(contents):
            contents = data.text()
            newItem = QTableWidgetItem(contents)
            selectedItemsList.append(newItem)
        return selectedItemsList

    def get_all_item_dic(self):
        """ 获取页面全部数据字典格式 """
        conten_dic = {}
        for j in range(0, self.rowCount()):
            buttonItems = []
            for i in range(0, 7):
                if not self.item(j, i) and i !=1 and i !=6:
                    break
                elif i == 1 or i == 6:  # 添加下拉框数据
                    text_list = []
                    for count in range(self.cellWidget(j,i).count()):
                        text_list.append(self.cellWidget(j,i).itemText(count))
                    currentText = self.cellWidget(j,i).currentText()
                    text_list.remove(currentText) # 移除选中的名称
                    text_list.insert(0,currentText) # 插入到数组第一个元素
                    text = ','.join(text_list)
                elif not self.item(j, i):
                    break
                elif self.item(j, i):   # 添加普遍数据
                    text =  self.item(j, i).text()

                buttonItems.append(text)
            if buttonItems:
                title = str(TFile.get_datetime(format_str="%f"))+str(j)
                print(buttonItems)
                conten_dic.update({str(title):{'name':buttonItems[0],"types":buttonItems[1],
                                    "content":buttonItems[2],"value":buttonItems[3],
                                    "sleep":buttonItems[4],"num":buttonItems[5],
                                    "exception":buttonItems[6]}})
        return conten_dic

    def delete_item(self):
        index_list = []
        try:
            for model_index in self.selectionModel().selectedRows():
                index = QtCore.QPersistentModelIndex(model_index)
                index_list.append(index)
            for index in index_list:
                self.removeRow(index.row())
            file_name = self.mainwindow.FileTreeView.currentItem().text()
            conten_dic = self.get_all_item_dic()
            TFile.set_json_record(file_name, conten_dic,is_clean=True)

        except:
            self.console_signal.emit('删除异常', 'red')

    def delete_all_item(self):
        self.clearContents()
        file_name = self.mainwindow.FileTreeView.currentItem().text()
        file_path = TFile.get_record_path()+file_name
        with open(file_path, 'wb+') as f:
            f.close()
        self.console_signal.emit('清空脚本文件:%s' % file_path, 'red')

    def set_ComboBox_value(self,item_list,x,y):
        """ 设置下拉框数据 """
        self.ComboBox = QComboBox()
        self.ComboBox.addItems(item_list)
        self.ComboBox.setStyleSheet('QComboBox{margin:3px};')
        self.setCellWidget(x, y, self.ComboBox)

    def get_ComboBox_value(self,rows):
        """ 根据行获取下拉框数据 """
        type_list  = []
        for count in range(self.cellWidget(int(rows),1).count()):
            type_list.append(self.cellWidget(rows,1).itemText(count))

        exception_list  = []
        for count in range(self.cellWidget(rows,6).count()):
            exception_list.append(self.cellWidget(rows,6).itemText(count))
        return type_list,exception_list

    def dropEvent(self, event: QDropEvent):
        """ 拖动事件 """

        if not event.isAccepted() and event.source() == self:
            drop_row = self.drop_on(event)
            rows = sorted(set(item.row() for item in self.selectedItems()))
            type_list,exception_list = self.get_ComboBox_value(rows[0])
            rows_to_move = [
                [QTableWidgetItem(self.item(row_index, column_index)) for column_index in range(self.columnCount())]
                for row_index in rows]
            for row_index in reversed(rows):
                self.removeRow(row_index)
                if row_index < drop_row:
                    drop_row -= 1
            for row_index, data in enumerate(rows_to_move):
                row_index += drop_row
                self.insertRow(row_index)
                for column_index, column_data in enumerate(data):
                    self.setItem(row_index, column_index, column_data)
                    print(row_index, column_index, column_data)
            self.set_ComboBox_value(type_list,row_index,1)
            self.set_ComboBox_value(exception_list,row_index,6)
            event.accept()

            TFile.set_json_record(self.mainwindow.FileTreeView.currentItem().text(), self.get_all_item_dic(),is_clean=True)
        super().dropEvent(event)

    def drop_on(self, event):
        try:
            index = self.indexAt(event.pos())
            if not index.isValid():
                return self.rowCount()
            return index.row() + 1 if self.is_below(event.pos(), index) else index.row()
        except:
            print('拖动异常2')

    def is_below(self, pos, index):
        try:
            rect = self.visualRect(index)
            margin = 2
            if pos.y() - rect.top() < margin:
                return False
            elif rect.bottom() - pos.y() < margin:
                return True
            return rect.contains(pos, True) and not (
                    int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()
        except:
            print('拖动异常3')

    def mouseReleaseEvent(self, event) -> None:
        TFile.set_json_record(self.mainwindow.FileTreeView.currentItem().text(), self.get_all_item_dic(),is_clean=True)

    def select_insert(self,buttonItems):
        select_rows = 0
        if len(self.selectedItems()) == 0:
            row = self.rowCount()  # 获取总共行数
            for j in range(0, row):
                items = self.item(j, 0)
                if items is None:
                    for i in range(0, len(buttonItems)):
                        self.table_write(j, i, buttonItems[i])
                    self.scrollToItem(self.selectRow(j),QAbstractItemView.PositionAtCenter)  # 自动滚动
                    break
        else:
            select_rows = (sorted(set(item.row() for item in self.selectedItems())))[0]
            self.insertRow(select_rows + 1)
            for i in range(0, len(buttonItems)):
                self.table_write(select_rows + 1, i, buttonItems[i])
        file_name = self.mainwindow.FileTreeView.currentItem().text()  #获取选择脚本文件的名称
        title = str(TFile.get_datetime())
        add_content = {str(title):{'name':buttonItems[0],"types":buttonItems[1],
                                   "content":buttonItems[2],"value":buttonItems[3],
                                   "sleep":buttonItems[4],"num":buttonItems[5],
                                   "exception":buttonItems[6]}}
        TFile.set_json_record(file_name, add_content)


"""
https://leetcode.com/problems/palindrome-partitioning/description/
"""
from typing import List

class Solution:

    def dfs(self, s, i, temp, result):
        if i >= len(s):
            result.append(temp[:])
            return
        for j in range(i, len(s)):
            if s[i:j+1] == s[i:j+1][::-1]:
                temp.append(s[i:j+1])
                self.dfs(s, j+1, temp, result)
                temp.pop()
    def partition(self, s: str) -> List[List[str]]:
        result = []
        self.dfs(s, 0, [], result)
        return result

solve = Solution()
print(solve.partition(s='aab'))


import pathlib
import requests
from pathlib import Path

def getLinkAndToken():
    path = Path(pathlib.Path.cwd(),'mispibot','src','main','resources','application.properties')
    print(str(path))
    paramlist = []
    with open(path) as file:
        for line in file:
            if 'bot.path' in line or 'bot.token' in line:
                paramlist.append(str(line.strip('\n').split('=')[1].strip()))

    print(paramlist)
    return paramlist

def regTelegramWebHook(linkFromNgrok,token):
    link = "https://api.telegram.org/bot"+token+"/setwebhook?url="+linkFromNgrok+"/bots/index.php"
    print(link)
    response = requests.get(link)
    valueslist = []
    for item in response.json().values():
        valueslist.append(item)

    if valueslist[0]:
        print("удачненько зарегались 👍")
    elif not valueslist[0]:
        print(response.text)
        print("не зарегано 👎")
    else:
        print(response.text)
        print('непонял ошибку☹☹')

def main():

    linkAndToken = getLinkAndToken()
    link = 0
    token = 0
    for item in linkAndToken:
        if 'https' in item:
            link = item
        else:
            token = item

    regTelegramWebHook(link,token)

if __name__ == "__main__":
    main()


# -*- coding: utf-8 -*-

from abc import ABCMeta, abstractmethod
import pandas as pd

from event import FillEvent
import datetime
import math

class ExecutionHandler(object, metaclass=ABCMeta):
    """
    ExecutionHandler抽象类处理由Portfolio生成的order对象
    与实际市场中发生的Fill对象之间的交互
    这个类可以用于实际的成交，或者模拟的成交
    """

    __metaclass__ = ABCMeta

    @abstractmethod
    def execute_order(self, event):
        """
        获取一个Order事件并执行，产生Fill事件放到事件队列中
        """
        raise NotImplementedError("Should implement execute_order()")

class SimulatedExecution(ExecutionHandler):
    """
    这是一个模拟的执行处理，简单的将所有的Order对象转化为等价的Fill对象
    不考虑时延，滑价以及成交比率的影响
    """

    def __init__(self, events, ContextInfo, bars, portfolio):
        self.events = events
        self.bars = bars
        self.portfolio = portfolio
        self.commission_rate = ContextInfo.commission_rate
        self.min_commission = ContextInfo.min_commission
        #self.execution_records = pd.DataFrame(
        #    columns=['date_time','symbol','order_price','order_amount','commission'])
        self.execution_records = []

    def update_temp_pos(self):
        """
        做一个临时的持仓表，以判断现金和持仓是否充足可用（实盘由交易所判断）
        在Portfolio处理完FILL事件后，重新获取temp_pos
        """
        #print(self.portfolio.positions)
        self.temp_pos = {}
        self.temp_pos['cash'] = self.portfolio.positions['cash']
        for sym in self.portfolio.symbol_list:
            self.temp_pos[sym] = self.portfolio.positions[sym]
        #print(self.temp_pos)
        #print(len(self.temp_pos))

    def execute_order(self, event):
        """
        接收Order事件，生成Fill事件
        """
        symbol = event.symbol
        order_amount = event.order_amount
        order_price = event.order_price

        cur_date = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        bar_date = self.bars.get_latest_bar(symbol)['date_time']
        last_close = self.bars.get_latest_bar(symbol)['close']

        # 判断是否停牌
        if self.bars.is_suspended_stock(symbol):
            print("[%s][股票当日停牌]Time：%s，Symbol：%s，Stock Suspended！"%(cur_date,bar_date,symbol))
            return

        # 判断是否涨/跌停（注意：未考虑创业/科创板的20%涨跌停及ST股的5%涨跌停）
        # 如果涨停买入或跌停卖出，则委托无效，不进行撮合
        if self.bars.get_latest_bar(symbol)['pct_chg'] >= 9.98 and order_amount > 0:
            print("[%s][股票当日涨停]Time：%s，Symbol：%s，Trade Limited！"%(cur_date,bar_date,symbol))
            return
        if self.bars.get_latest_bar(symbol)['pct_chg'] <= -9.98 and order_amount < 0:
            print("[%s][股票当日跌停]Time：%s，Symbol：%s，Trade Limited！"%(cur_date,bar_date,symbol))
            return

        # 市价单：委托价为收盘价
        if order_price == 'MARKET':
            order_price = last_close

        # 股价判断：买入/卖出价若低于/高于收盘价，则按收盘价执行
        if order_amount < 0 and order_price > last_close:
            order_price = last_close
        if order_amount > 0 and order_price < last_close:
            order_price = last_close

        # 处理order_amount，向下取整百（需要用floor处理负数）
        order_amount = math.floor(order_amount/100)*100
        if order_amount == 0: # 委托数量为0，则中止运行
            print("[%s][委托数量错误]Time：%s，Symbol：%s，Price：%s，Qty：%s，Quantity Error！"%(cur_date,bar_date,symbol,order_price,order_amount))
            return

        # 计算手续费=MAX（交易额*佣金率，最低手续费）
        commission = max(round(order_price*abs(order_amount)*self.commission_rate,2),self.min_commission)

        # 判断现金及持仓是否可用
        if order_amount > 0: # 若买入，判断可用资金（暂不考虑手续费，现金可能出现负数）
            #if order_amount*order_price+commission > self.temp_pos['cash']:
            if order_amount*order_price > self.temp_pos['cash']:
                # 可用资金不足，不执行买入
                print("[%s][可用资金不足]Time：%s，Symbol：%s，Price：%s，Qty：%s，Cash Error！"%(cur_date,bar_date,symbol,order_price,order_amount))
                return
        elif order_amount < 0: # 若卖出，判断可用数量
            if order_amount < -self.temp_pos[symbol]:
                # 可用数量不足，拒绝执行卖出
                print("[%s][可用持仓不足]Time：%s，Symbol：%s，Price：%s，Qty：%s，Position Error！"%(cur_date,bar_date,symbol,order_price,order_amount))
                return

        # 交易撮合，生成FILL事件
        fill_event = FillEvent(symbol,order_amount,order_price,commission)
        self.events.put(fill_event)
        print("[%s][委托交易成功]Time：%s，Symbol：%s，Price：%s，Qty：%s"%(cur_date,bar_date,symbol,order_price,order_amount))

        # 实时更新至temp_pos，以便验证每个订单的持仓及金额是否可用
        # 此处遇到问题：如果直接令temp_pos=portfolio.positions，
        # 当temp_pos变动时，portfolio也会发生变动，
        # 而新建一个temp_pos字典可以解决这个问题，具体原因不明
        # print(self.portfolio.positions)
        self.temp_pos['cash'] -= order_price*order_amount+commission
        self.temp_pos[symbol] += order_amount
        # print(self.portfolio.positions)

        # 写入交易记录
        record = {}
        record['date_time'] = bar_date
        record['symbol'] = symbol
        record['order_price'] = order_price
        record['order_amount'] = order_amount
        record['commission'] = commission
        #self.execution_records = self.execution_records.append(record, ignore_index=True)
        self.execution_records.append(record)

    def download_records(self):
        """
        导出交易记录
        """
        execution_records = pd.DataFrame(self.execution_records)
        execution_records.to_csv('logs/execution_records.csv')


from runner import Runner
from data.data_loader import DataLoader
from ekphrasis.classes.preprocessor import TextPreProcessor
from ekphrasis.classes.tokenizer import SocialTokenizer
from ekphrasis.dicts.emoticons import emoticons
from etc.logger import Logger
from feature_extraction.feature_extractor import FeatureExtractor
import random as rn
import numpy as np
import tensorflow as tf
import os

# add seeds for reproducibility
SEED = 1337
os.environ['PYTHONHASHSEED'] = '0'
np.random.seed(SEED)
rn.seed(SEED)
tf.set_random_seed(SEED)

TERNARY = True

# preprocessor for training/test
preprocessor = TextPreProcessor(
    normalize=['url', 'email', 'percent', 'money', 'phone', 'user', 'time',
               'date', 'number'],
    annotate={'hashtag', 'allcaps', 'elongated', "repeated", 'emphasis',
              'censored'},
    fix_html=True,
    segmenter="twitter",
    corrector="twitter",
    unpack_hashtags=True,
    unpack_contractions=True,
    spell_correct_elong=False,
    tokenizer=SocialTokenizer(lowercase=True).tokenize,
    dicts=[emoticons])

# preprocessor for features extracting
feature_preprocessor = TextPreProcessor(
    normalize=['url', 'email', 'percent', 'money', 'phone', 'user', 'time',
               'date', 'number'],
    fix_html=True,
    segmenter='twitter',
    corrector='twitter',
    unpack_contractions=True,
    spell_correct_elong=False,
    tokenizer=SocialTokenizer(lowercase=True).tokenize,
    dicts=[emoticons])

logger = Logger()
runner = Runner(logger=logger, ternary=TERNARY,
                model_type='baseline', use_embeddings=True)

logger.write('preprocessing: %s' % (True if preprocessor else False))

data_loader = DataLoader(preprocessor=preprocessor)
train, test = data_loader.get_train_test(ternary=TERNARY)
extra_train = data_loader.get_train(ternary=TERNARY, \
    paths=['data/ydata-ynacc-v1_0_expert_annotations_filt.tsv'])

feature_data_loader = DataLoader(preprocessor=feature_preprocessor)
feature_extractor = FeatureExtractor(data_loader=feature_data_loader,
                                     logger=logger)

train_feats, test_feats = feature_extractor.get_train_test_features(
    ternary=TERNARY,
    manual=True,
    auto=True,
    scaled=False)

runner.run(train, test, extra_train=extra_train)