from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *

from swatchM8.core.util.Helpers import convertToQColor

from swatchM8.ui.qt.widgets.ColorPickerWidget import ColorPickerWidget

from PySide2 import QtCore, QtGui, QtWidgets

class AddPartWidget(QWidget):

    def __init__(self, MainUI, Main):
        # type: (object, object) -> None

        super(AddPartWidget, self).__init__(None)  # Calls the init of the super class -> QWidget

        self.setupUi()
        self.connectElements()
        self.connectInputChecks()
        self.MainUI = MainUI
        self.Main = Main

        layout = QHBoxLayout()
        layout.setMargin(0)
        layout.addWidget(self.AddPartFrame)
        self.setLayout(layout)

        self.ColorPicker = ColorPickerWidget()
        self.ColorPickerPlaceHolder.setLayout(self.ColorPicker)

        self.ModelComboBox.setCurrentIndex(0)

        self.MainUI.setInputInvalid(self.ModelNameLineEdit)
        self.MainUI.setInputInvalid(self.PartNameLineEdit)

        self.UniqueSwatchRadioButton.setChecked(True)
        self.setUniqueSwatchSetup()

    def setupUi(self):
        self.AddPartFrame = QtWidgets.QFrame()
        self.AddPartFrame.setGeometry(QtCore.QRect(0, 0, 311, 261))
        self.AddPartFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.AddPartFrame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.AddPartFrame.setObjectName("AddPartFrame")
        self.AddPartLabel = QtWidgets.QLabel(self.AddPartFrame)
        self.AddPartLabel.setGeometry(QtCore.QRect(10, 10, 141, 21))
        font = QtGui.QFont()
        font.setPointSize(16)
        self.AddPartLabel.setFont(font)
        self.AddPartLabel.setObjectName("AddPartLabel")
        self.CreateButton = QtWidgets.QPushButton(self.AddPartFrame)
        self.CreateButton.setGeometry(QtCore.QRect(200, 180, 81, 71))
        self.CreateButton.setObjectName("CreateButton")
        self.ColorPickerPlaceHolder = QtWidgets.QFrame(self.AddPartFrame)
        self.ColorPickerPlaceHolder.setGeometry(QtCore.QRect(10, 170, 181, 81))
        self.ColorPickerPlaceHolder.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.ColorPickerPlaceHolder.setFrameShadow(QtWidgets.QFrame.Raised)
        self.ColorPickerPlaceHolder.setObjectName("ColorPickerPlaceHolder")
        self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.AddPartFrame)
        self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(10, 40, 271, 31))
        self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
        self.ModelLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
        self.ModelLayout.setContentsMargins(-1, 2, -1, 2)
        self.ModelLayout.setObjectName("ModelLayout")
        self.ModelLabel = QtWidgets.QLabel(self.horizontalLayoutWidget_2)
        self.ModelLabel.setObjectName("ModelLabel")
        self.ModelLayout.addWidget(self.ModelLabel)
        self.ModelStackedWidget = QtWidgets.QStackedWidget(self.horizontalLayoutWidget_2)
        self.ModelStackedWidget.setToolTipDuration(0)
        self.ModelStackedWidget.setObjectName("ModelStackedWidget")
        self.page_5 = QtWidgets.QWidget()
        self.page_5.setObjectName("page_5")
        self.ModelComboBox = QtWidgets.QComboBox(self.page_5)
        self.ModelComboBox.setGeometry(QtCore.QRect(18, 3, 141, 21))
        self.ModelComboBox.setObjectName("ModelComboBox")
        self.NewModelButton = QtWidgets.QPushButton(self.page_5)
        self.NewModelButton.setGeometry(QtCore.QRect(160, 2, 51, 23))
        self.NewModelButton.setObjectName("NewModelButton")
        self.ModelStackedWidget.addWidget(self.page_5)
        self.page_6 = QtWidgets.QWidget()
        self.page_6.setObjectName("page_6")
        self.ModelNameLineEdit = QtWidgets.QLineEdit(self.page_6)
        self.ModelNameLineEdit.setGeometry(QtCore.QRect(20, 3, 141, 21))
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.ModelNameLineEdit.sizePolicy().hasHeightForWidth())
        self.ModelNameLineEdit.setSizePolicy(sizePolicy)
        self.ModelNameLineEdit.setMaximumSize(QtCore.QSize(240, 16777215))
        self.ModelNameLineEdit.setReadOnly(False)
        self.ModelNameLineEdit.setObjectName("ModelNameLineEdit")
        self.ExistingModelButton = QtWidgets.QPushButton(self.page_6)
        self.ExistingModelButton.setGeometry(QtCore.QRect(160, 2, 61, 23))
        self.ExistingModelButton.setObjectName("ExistingModelButton")
        self.ModelStackedWidget.addWidget(self.page_6)
        self.ModelLayout.addWidget(self.ModelStackedWidget)
        self.horizontalLayoutWidget_3 = QtWidgets.QWidget(self.AddPartFrame)
        self.horizontalLayoutWidget_3.setGeometry(QtCore.QRect(10, 80, 271, 23))
        self.horizontalLayoutWidget_3.setObjectName("horizontalLayoutWidget_3")
        self.PartLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_3)
        self.PartLayout.setContentsMargins(-1, 2, 2, 2)
        self.PartLayout.setObjectName("PartLayout")
        self.PartNameLabel = QtWidgets.QLabel(self.horizontalLayoutWidget_3)
        self.PartNameLabel.setObjectName("PartNameLabel")
        self.PartLayout.addWidget(self.PartNameLabel)
        self.PartNameLineEdit = QtWidgets.QLineEdit(self.horizontalLayoutWidget_3)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.PartNameLineEdit.sizePolicy().hasHeightForWidth())
        self.PartNameLineEdit.setSizePolicy(sizePolicy)
        self.PartNameLineEdit.setMaximumSize(QtCore.QSize(240, 16777215))
        self.PartNameLineEdit.setReadOnly(False)
        self.PartNameLineEdit.setObjectName("PartNameLineEdit")
        self.PartLayout.addWidget(self.PartNameLineEdit)
        self.horizontalLayoutWidget = QtWidgets.QWidget(self.AddPartFrame)
        self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 140, 271, 31))
        self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
        self.NameLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
        self.NameLayout.setContentsMargins(-1, 2, -1, 2)
        self.NameLayout.setObjectName("NameLayout")
        self.SwatchLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
        self.SwatchLabel.setObjectName("SwatchLabel")
        self.NameLayout.addWidget(self.SwatchLabel)
        self.SwatchStackedWidget = QtWidgets.QStackedWidget(self.horizontalLayoutWidget)
        self.SwatchStackedWidget.setObjectName("SwatchStackedWidget")
        self.page_3 = QtWidgets.QWidget()
        self.page_3.setObjectName("page_3")
        self.SwatchComboBox = QtWidgets.QComboBox(self.page_3)
        self.SwatchComboBox.setGeometry(QtCore.QRect(0, 3, 171, 21))
        self.SwatchComboBox.setObjectName("SwatchComboBox")
        self.NewSwatchButton = QtWidgets.QPushButton(self.page_3)
        self.NewSwatchButton.setGeometry(QtCore.QRect(171, 2, 51, 23))
        self.NewSwatchButton.setObjectName("NewSwatchButton")
        self.SwatchStackedWidget.addWidget(self.page_3)
        self.page_4 = QtWidgets.QWidget()
        self.page_4.setObjectName("page_4")
        self.SwatchNameLineEdit = QtWidgets.QLineEdit(self.page_4)
        self.SwatchNameLineEdit.setGeometry(QtCore.QRect(0, 3, 161, 21))
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.SwatchNameLineEdit.sizePolicy().hasHeightForWidth())
        self.SwatchNameLineEdit.setSizePolicy(sizePolicy)
        self.SwatchNameLineEdit.setMaximumSize(QtCore.QSize(240, 16777215))
        self.SwatchNameLineEdit.setReadOnly(False)
        self.SwatchNameLineEdit.setObjectName("SwatchNameLineEdit")
        self.ExistingSwatchButton = QtWidgets.QPushButton(self.page_4)
        self.ExistingSwatchButton.setGeometry(QtCore.QRect(162, 2, 51, 23))
        self.ExistingSwatchButton.setObjectName("ExistingSwatchButton")
        self.SwatchStackedWidget.addWidget(self.page_4)
        self.NameLayout.addWidget(self.SwatchStackedWidget)
        self.horizontalLayoutWidget_4 = QtWidgets.QWidget(self.AddPartFrame)
        self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(10, 120, 211, 21))
        self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4")
        self.RadioButtonLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_4)
        self.RadioButtonLayout.setContentsMargins(0, 0, 0, 0)
        self.RadioButtonLayout.setObjectName("RadioButtonLayout")
        self.UniqueSwatchRadioButton = QtWidgets.QRadioButton(self.horizontalLayoutWidget_4)
        self.UniqueSwatchRadioButton.setObjectName("UniqueSwatchRadioButton")
        self.RadioButtonLayout.addWidget(self.UniqueSwatchRadioButton)
        self.SharedSwatchRadioButton = QtWidgets.QRadioButton(self.horizontalLayoutWidget_4)
        self.SharedSwatchRadioButton.setObjectName("SharedSwatchRadioButton")
        self.RadioButtonLayout.addWidget(self.SharedSwatchRadioButton)
        self.line = QtWidgets.QFrame(self.AddPartFrame)
        self.line.setGeometry(QtCore.QRect(10, 108, 271, 8))
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")

        self.retranslateUi()
        self.ModelStackedWidget.setCurrentIndex(0)
        self.SwatchStackedWidget.setCurrentIndex(0)

    def retranslateUi(self):
        self.AddPartLabel.setText(QtWidgets.QApplication.translate("FormName", "Add Part", None, -1))
        self.CreateButton.setText(QtWidgets.QApplication.translate("FormName", "Create", None, -1))
        self.ModelLabel.setText(QtWidgets.QApplication.translate("FormName", "Model:", None, -1))
        self.NewModelButton.setText(QtWidgets.QApplication.translate("FormName", "New", None, -1))
        self.ExistingModelButton.setText(QtWidgets.QApplication.translate("FormName", "Pick", None, -1))
        self.PartNameLabel.setText(QtWidgets.QApplication.translate("FormName", "PartName:", None, -1))
        self.SwatchLabel.setText(QtWidgets.QApplication.translate("FormName", "Swatch:", None, -1))
        self.NewSwatchButton.setText(QtWidgets.QApplication.translate("FormName", "New", None, -1))
        self.ExistingSwatchButton.setText(QtWidgets.QApplication.translate("FormName", "Pick", None, -1))
        self.UniqueSwatchRadioButton.setText(QtWidgets.QApplication.translate("FormName", "Unique Swatch", None, -1))
        self.SharedSwatchRadioButton.setText(QtWidgets.QApplication.translate("FormName", "Shared Swatch", None, -1))

    def connectElements(self):
        # type: () -> None

        self.NewSwatchButton.clicked.connect(self.newSwatchButton)
        self.NewModelButton.clicked.connect(self.newModelButton)
        self.ExistingSwatchButton.clicked.connect(self.existingSwatchButton)
        self.ExistingModelButton.clicked.connect(self.existingModelButton)

        self.SwatchComboBox.currentIndexChanged.connect(self.updateSwatch)

        self.CreateButton.clicked.connect(self.addButton)
        self.UniqueSwatchRadioButton.toggled.connect(self.setUniqueSwatchSetup)
        self.SharedSwatchRadioButton.toggled.connect(self.setSharedSwatchSetup)

        self.ModelComboBox.currentIndexChanged.connect(self.generateUniqueSwatchName)
        self.ModelNameLineEdit.textEdited.connect(self.generateUniqueSwatchName)
        self.PartNameLineEdit.textEdited.connect(self.generateUniqueSwatchName)

    def connectInputChecks(self):

        self.ModelComboBox.currentIndexChanged.connect(lambda: self.MainUI.checkIfPartNameEntryIsValid(self.PartNameLineEdit, self.ModelComboBox.currentData()))
        self.ModelNameLineEdit.textChanged.connect(lambda: self.MainUI.checkIfModelNameEntryIsValid(self.ModelNameLineEdit))
        self.PartNameLineEdit.textChanged.connect(self.inputCheckPartName)
        self.SwatchNameLineEdit.textChanged.connect(self.inputCheckSwatchName)

    def inputCheckPartName(self):

        if self.PartNameLineEdit.text() == "":
            self.MainUI.setInputInvalid(self.PartNameLineEdit)
            return

        if self.ModelStackedWidget.currentIndex() == 1:  # If new Model aka no parts yet
            self.MainUI.setInputValid(self.PartNameLineEdit)
            return

        self.MainUI.checkIfPartNameEntryIsValid(self.PartNameLineEdit, self.ModelComboBox.currentData())

    def inputCheckSwatchName(self):

        if self.SwatchStackedWidget.currentIndex() == 0:  # als er een gepicked word, geen check
            return

        if self.SwatchNameLineEdit.text() == "" and self.SharedSwatchRadioButton.isChecked():  # Leeg en customName, sws niet valid
            self.MainUI.setInputInvalid(self.SwatchNameLineEdit)
            return

        if self.Main.texture == None:  # Bij init code geeft hij anders een Attribute error
            return
        self.MainUI.checkifSwatchNameIsValid(self.SwatchNameLineEdit)

    def setNewData(self):
        # type: () -> None

        self.updateModelDropdown()

        if self.ModelComboBox.count() == 0:
            self.newModelButton()

        self.updateSwatchDropdown()

        self.ColorPicker.setNewData(QColor('white'))

        # self.updateSwatch(self.SwatchComboBox.currentIndex())

    def updateSwatchDropdown(self):
        # type: () -> None

        self.SwatchComboBox.clear()
        for index, item in enumerate(self.Main.texture.swatches):
            self.SwatchComboBox.addItem(item.name)
            self.SwatchComboBox.setItemData(index, item)

    def updateModelDropdown(self):
        # type: () -> None

        self.ModelComboBox.clear()
        for index, item in enumerate(self.Main.models):
            self.ModelComboBox.addItem(item.name)
            self.ModelComboBox.setItemData(index, item)

    def updateSwatch(self, index):
        # type: (int) -> None

        selectedSwatch = self.SwatchComboBox.itemData(index)
        if selectedSwatch != None:
            self.ColorPicker.setNewData(convertToQColor(selectedSwatch.color))
            # self.ColorPicker.setNewData(QColor('white'))

    def newSwatchButton(self):
        # type: () -> None

        self.SwatchStackedWidget.setCurrentIndex(1)
        self.ColorPicker.setNewData(QColor('white'))
        self.ColorPickerPlaceHolder.setEnabled(True)

    def newModelButton(self):
        # type: () -> None

        self.ModelStackedWidget.setCurrentIndex(1)

    def setUniqueSwatchSetup(self):
        # type: () -> None

        self.SwatchStackedWidget.setCurrentIndex(1)
        self.ExistingSwatchButton.setEnabled(0)
        self.SwatchNameLineEdit.setEnabled(0)
        self.ColorPickerPlaceHolder.setEnabled(True)
        self.ColorPicker.setNewData(QColor('white'))
        self.generateUniqueSwatchName()

    def setSharedSwatchSetup(self):
        # type: () -> None

        self.ExistingSwatchButton.setEnabled(1)
        self.SwatchNameLineEdit.setEnabled(1)
        self.SwatchNameLineEdit.setText('')

    def generateUniqueSwatchName(self):
        # type: () -> None

        if self.UniqueSwatchRadioButton.isChecked():

            if self.ModelStackedWidget.currentIndex() == 0:

                modelName = self.ModelComboBox.currentText()

            else:
                modelName = self.ModelNameLineEdit.text()

            partName = self.PartNameLineEdit.text()

            if partName == '':
                partName = 'PartName'

            if modelName == '':
                modelName = 'ModelName'

            self.SwatchNameLineEdit.setText(modelName + "-" + partName)

    def existingSwatchButton(self):
        # type: () -> None

        self.updateSwatch(self.SwatchComboBox.currentIndex())
        self.SwatchStackedWidget.setCurrentIndex(0)
        self.ColorPickerPlaceHolder.setEnabled(False)

    def existingModelButton(self):
        # type: () -> None

        self.ModelStackedWidget.setCurrentIndex(0)

    def addButton(self):
        # type: () -> None

        color = self.ColorPicker.getPickedColor()

        if self.ModelStackedWidget.currentIndex() == 0:
            model = self.ModelComboBox.currentData()
        else:
            model = self.Main.createModel(self.ModelNameLineEdit.text())

        if self.SwatchStackedWidget.currentIndex() == 0:
            swatch = self.SwatchComboBox.itemData(self.SwatchComboBox.currentIndex())
        else:
            swatch = self.Main.texture.createSwatch(self.SwatchNameLineEdit.text(), color)

        # TODO: model verwijderen als het fout loopt bij de swatch

        self.Main.addPartToSwatch(swatch, model, self.PartNameLineEdit.text())
        self.Main.app.unwrapSelection(swatch.coordinate, self.Main.texture.amountOfSwatchesOnRow)

        self.MainUI.applyChanges()
        self.reset()

    def reset(self):
        # type: () -> None

        self.SwatchNameLineEdit.setText('')
        self.ModelNameLineEdit.setText('')
        self.PartNameLineEdit.setText('')
        self.ModelStackedWidget.setCurrentIndex(0)

        self.setNewData()

    def setComboBoxModel(self, model):
        # if the add part button is pressed trought the edit model frame, the correct model will be set

        index = self.ModelComboBox.findData(model)
        self.ModelComboBox.setCurrentIndex(index)


import mne
from mne.time_frequency import psd_array_multitaper

from itertools import combinations
from scipy.stats import ttest_rel
import scipy
import pandas as pd
import numpy as np
from tqdm.notebook import tqdm
from scipy.interpolate import interp1d

from sklearn.metrics import accuracy_score, f1_score, precision_score
from sklearn.metrics import recall_score

from functools import partial
from pathlib import Path

trim_mean = partial(scipy.stats.trim_mean, proportiontocut=0.1)

def get_spindles_annotations(raw, l_freq=10, h_freq=15, win_width=0.75,
                             ch_name=None, low_th=1.2, high_th=3.5,
                             min_duration=0.5, max_duration=10.0,
                             robust=False):
    resamp_freq = 50

    # 10-15 Hz bandpass
    if ch_name is None:
        ch_name = [ch_name for ch_name in raw.ch_names
                   if "eeg" in ch_name.lower() or "channel" in ch_name.lower()][0]
    bp_data = raw.copy().pick(ch_name).load_data().filter(l_freq, h_freq)
    bp_data = bp_data.resample(resamp_freq)
    times = bp_data.times
    bp_data = bp_data.get_data().squeeze()

    series = pd.Series(bp_data**2, index=times)

    # Rolling windows for MS computation
    rolling = series.rolling(int(win_width*resamp_freq), 1, True)
    if robust:
        res = np.sqrt(rolling.agg(trim_mean))**3  # cubed RMS
    else:
        res = np.sqrt(rolling.mean())**3  # cubed RMS

    # Rolling window for computing the average MS signal amplitude
    page_width = 10*60
    rolling = series.rolling(int(page_width*resamp_freq), 1, True)
    if robust:
        res_page = np.sqrt(rolling.agg(trim_mean))**3  # cubed RMS
    else:
        res_page = np.sqrt(rolling.mean())**3  # cubed RMS

    # Thresholding
    df = pd.DataFrame({"high": res > high_th*res_page,
                       "low": res > low_th*res_page})

    # Making windows based on the low threshold
    mask = df.low.diff().fillna(False)

    # Force transitions if starts or ends with True values
    mask.iloc[0] = df.low.values[0]
    mask.iloc[-1] = df.low.values[-1]

    tmp = df.index[mask]
    starts = tmp[::2]
    stops = tmp[1::2]

    # Selecting only the windows with a peak above the high threshold
    tmp = np.array([(start, stop) for start, stop in zip(starts, stops)
                    if np.sum(df.loc[start:stop, "high"])])
    if not len(tmp):
        return mne.Annotations([], [], [], orig_time=raw.info["meas_date"])

    # Checking window duration based on the low threshold
    durations = np.diff(tmp).squeeze()
    valid_windows = tmp[(durations >= min_duration) &
                        (durations <= max_duration), :]
    if not len(valid_windows):
        return mne.Annotations([], [], [], orig_time=raw.info["meas_date"])

    assert np.all(np.diff(valid_windows).squeeze() >= min_duration)

    return mne.Annotations(valid_windows[:, 0],
                           np.diff(valid_windows).squeeze(),
                           "spindle", orig_time=raw.info["meas_date"])

def get_manual_annotations(file_path, raw):

    spin_day1 = pd.read_excel(file_path)
    spin_day1 = spin_day1.drop(0, axis=0)
    spin_day1 = spin_day1[['Start Time', 'End Time']]

    spin_day1.columns = ["start", "stop"]

    spin_day1["meas_date"] = raw.info["meas_date"]
    spin_day1.start = spin_day1.start.astype(spin_day1.meas_date.dtype)
    spin_day1.stop = spin_day1.stop.astype(spin_day1.meas_date.dtype)

    starts = (spin_day1.start-spin_day1.meas_date)
    starts = starts.astype('timedelta64[ms]')/1000
    stops = (spin_day1.stop-spin_day1.meas_date)
    stops = stops.astype('timedelta64[ms]')/1000

    return mne.Annotations(starts, stops-starts, "spindle_manual",
                           orig_time=raw.info["meas_date"])

def fix_edf(stages_df):
    vals, counts = np.unique(stages_df.start, return_counts=True)
    if len(np.unique(counts)) == 1 and np.unique(counts)[0] == 1:
        return stages_df  # nothing to fix
    for val in vals[np.where(counts != 6)]:
        stages_df = stages_df[stages_df.start != val]
    for i in range(1, 6):
        assert np.all(stages_df.iloc[::6]["start"].values ==
                      stages_df.iloc[i::6]["start"].values)
        stages_df.iloc[i::6].loc[:, "start"] += 10*i
    return stages_df

def get_sleep_stages_df(file_path, raw, stage_duration=10.0):
    if file_path.suffix == ".csv":
        stages = pd.read_csv(file_path)
    else:
        stages = pd.read_excel(file_path)

    stages = stages.drop(index=0)[["Rodent Sleep" if "Rodent Sleep" in stages else "Label",
                                   "Start Time" if "Start Time" in stages else "Time Stamp"]]

    stages.columns = ["label", "start"]
    starts = stages.start.astype('datetime64[ns, UTC]')
    stages.start = (starts - raw.info["meas_date"]).astype('timedelta64[s]')
    if file_path.suffix == ".csv":
        stages = fix_edf(stages)
    return stages

def get_sleep_stages(file_path, raw, stage_duration=10.0):
    stages = get_sleep_stages_df(file_path, raw, stage_duration)

    return np.sum([mne.Annotations(stages.start[stages.label == stage],
                                   stage_duration,
                                   f"stage_{stage}",
                                   orig_time=raw.info["meas_date"])
                   for stage in stages.label.unique()])

def filter_annotations(annotations, tmin, tmax):
    # keep annotation which middle point is within tmin and tmax, without
    # croping them if they party overlap outside of this range
    mid_points = [annot["onset"] + annot["duration"]/2
                  for annot in annotations]
    dat = np.array([(annot["onset"], annot["duration"], annot["description"])
                    for annot, mid_point in zip(annotations, mid_points)
                    if tmin < mid_point < tmax]).T
    if len(dat):
        onset, duration, description = dat
        return mne.Annotations(onset, duration, description,
                               orig_time=annotations.orig_time)
    return mne.Annotations([], [], [], orig_time=annotations.orig_time)

def filter_annots_per_stage(annot_to_filter, annot_stages, stage_in="stage_S"):
    if not len(annot_to_filter):
        return annot_to_filter
    annots = {"onset": [annot["onset"] for annot in annot_stages],
              "duration": [annot["duration"] for annot in annot_stages],
              "description": [annot["description"] for annot in annot_stages]}
    annots = pd.DataFrame(annots)

    cond = annots.description == stage_in
    discontinuities = np.where(np.diff(annots[cond].onset) != 10)[0]+1
    discontinuities = np.concatenate([[0], discontinuities, [np.sum(cond)]])
    block_lenghts = np.diff(discontinuities)*10
    block_starts = annots[cond].onset.values[discontinuities[:-1]]

    lst_annots = [filter_annotations(annot_to_filter, start, start+duration)
                  for start, duration in zip(block_starts, block_lenghts)]
    annotations = np.sum(lst_annots)
    if not isinstance(annotations, mne.Annotations):
        # Happens when there is not annotations
        return lst_annots[0]
    return annotations

def file_list_to_dict(files):
    keys = [(path.parent.name, path.name.split(".")[-2].split("_")[-1])
            for path in files]
    return dict(zip(keys, files))

def get_annotated_raw(edf_file, spindle_file, sleep_file,
                      ch_name=None):
    raw = mne.io.read_raw_edf(edf_file)

    spin_annotations = get_spindles_annotations(raw, ch_name=ch_name)
    annotations_manual = get_manual_annotations(spindle_file, raw)
    annotations_stages = get_sleep_stages(sleep_file, raw)

    spin_annotations = filter_annots_per_stage(spin_annotations,
                                               annotations_stages)

    print([raw.annotations, spin_annotations,
           annotations_manual, annotations_stages])
    raw.set_annotations(raw.annotations + spin_annotations +
                        annotations_manual + annotations_stages)
    return raw

def get_sample_df(raw, sleep_file):
    sample_df = pd.DataFrame({"time": raw.times[::10]})
    sample_df["spindle"] = False
    sample_df["spindle_manual"] = False
    sample_df = sample_df.set_index("time")

    annot_df = raw.annotations.to_data_frame()
    starts = annot_df.onset.astype('datetime64[ns, UTC]')
    annot_df["start"] = (starts-raw.info["meas_date"])
    annot_df["start"] = annot_df["start"].astype('timedelta64[ms]')/1000
    annot_df["stop"] = annot_df["start"] + annot_df.duration

    for _, row in annot_df.iterrows():
        sample_df.loc[row.start:row.stop, row.description] = True

    stages = pd.read_excel(sleep_file)
    stages = stages.drop(index=0)[["Label", "Start Time"]]
    stages.columns = ["label", "start"]
    starts = stages.start.astype('datetime64[ns, UTC]')
    stages.start = (starts-raw.info["meas_date"]).astype('timedelta64[s]')
    stage_duration = 10.0

    sample_df["nrem"] = False
    sample_df["rem"] = False
    for _, row in stages.iterrows():
        if row.label == "S":
            sample_df.loc[row.start:row.start+stage_duration, "nrem"] = True
        if row.label == "P":
            sample_df.loc[row.start:row.start+stage_duration, "rem"] = True

    return sample_df

def get_performances(sample_df):
    return {"accuracy": accuracy_score(sample_df.spindle_manual,
                                       sample_df.spindle),
            "f1": f1_score(sample_df.spindle_manual,
                           sample_df.spindle),
            "precision": precision_score(sample_df.spindle_manual,
                                         sample_df.spindle),
            "recall": recall_score(sample_df.spindle_manual,
                                   sample_df.spindle)}

def get_spin_features(raw, event_name, l_freq=10,
                      h_freq=15, ch_name="EEG 1"):
    bp_data = raw.copy().pick(ch_name).load_data().filter(l_freq, h_freq)

    target_freqs = np.arange(10, 15.1, 0.1)
    p2p = []
    powers = []
    for annot in tqdm(raw.annotations):
        if annot["description"] == event_name:
            dat = bp_data.get_data(tmin=annot["onset"],
                                   tmax=annot["onset"]+annot["duration"])
            p2p.append(dat.max()-dat.min())
            power, freqs = psd_array_multitaper(dat, raw.info["sfreq"],
                                                fmin=5, fmax=20, verbose=False)
            try:
                f = interp1d(freqs, power, kind='cubic')
                powers.append(f(target_freqs).squeeze())
            except ValueError:
                powers.append(target_freqs*np.nan)

    spin_feat = pd.DataFrame([(annot["onset"], annot["duration"])
                              for annot in raw.annotations
                              if annot["description"] == event_name],
                             columns=["onset", "duration"])

    # The two systems recorded in different units
    if np.mean(p2p) > .005:
        spin_feat["p2p_amp"] = np.array(p2p)*1e3  # In uV
    elif np.mean(p2p) < .001:
        spin_feat["p2p_amp"] = np.array(p2p)*1e6  # In uV
    elif len(p2p) == 0:
        spin_feat["p2p_amp"] = []
    else:
        raise ValueError(f"Check units. Mean p2p == {np.mean(p2p)}; p2p={p2p}")

    for freq, power_values in zip(target_freqs, np.array(powers).T):
        spin_feat[str(np.round(freq, 1))] = power_values

    spin_feat["event"] = event_name

    return spin_feat

def get_spin_features_events(raw, ch_name, event_names):
    return pd.concat([get_spin_features(raw, event_name, ch_name=ch_name)
                      for event_name in event_names])

def get_plotting_data(raw, ch_name, nb_hours=4, bins_per_hours=5,
                      event_kinds=("spindle", "spindle_manual")):
    feat = get_spin_features_events(raw, ch_name, event_kinds)

    bins = np.linspace(0, nb_hours*60*60, nb_hours*bins_per_hours+1)
    bin_labels = (bins[1:] + bins[:-1])/2/60/60
    feat["bins"] = pd.cut(feat["onset"], bins=bins,
                          labels=bin_labels, right=False)

    target_freqs = feat.loc[:, "10.0":"15.0"].columns
    groups = feat.drop(columns=target_freqs)\
                 .melt(id_vars=["onset", "event", "bins"])\
                 .groupby(["event", "bins", "variable"])
    data = groups.mean().drop(columns="onset")
    data_count = groups.count().drop(columns="onset").reset_index()
    data_count = data_count[data_count.variable == "duration"]
    data_count["variable"] = "count"

    sleep_durations = {}
    for tmin, tmax, bin in zip(bins[:-1], bins[1:], bin_labels):
        loop_annots = raw.annotations.copy().crop(tmin=tmin, tmax=tmax,
                                                  use_orig_time=False)
        sleep_durations[bin] = np.sum([annot["duration"]
                                       for annot in loop_annots
                                       if annot["description"] == "stage_S"])
    sleep_durations = pd.Series(sleep_durations).reset_index()
    sleep_durations.columns = ["bins", "sleep_duration"]

    sleep_durations.bins = pd.Categorical(sleep_durations.bins.astype(float))

    data_density = data_count.copy().merge(sleep_durations, on="bins")
    data_density["value"] /= data_density.sleep_duration/60
    data_density = data_density.drop(columns="sleep_duration")
    data_density["variable"] = "density"

    data = pd.concat([data,
                      data_count.set_index(["event", "bins", "variable"]),
                      data_density.set_index(["event", "bins", "variable"])])
    data = data.sort_values(['event', 'variable', 'bins'])
    return data.reset_index(), feat.drop(columns=["bins"]).reset_index()

def get_sleep_duration_df(sleep_files):
    sleep_duration = {key: np.sum(pd.read_excel(path).iloc[1:, 0] == "S")/6 for key, path in sleep_files.items()}
    sleep_duration = pd.Series(sleep_duration).reset_index()
    sleep_duration.columns = ["animal", "condition", "nrem_min"]
    #sleep_duration.loc[sleep_duration.condition == "Vehicle 1", "condition"] = "Vehicle"
    #sleep_duration.loc[sleep_duration.condition == "Vehicle 2", "condition"] = "Vehicle"
    sleep_duration.loc[sleep_duration.condition == "Intermittent 1", "condition"] = "Intermittent"
    sleep_duration.loc[sleep_duration.condition == "Intermittent 2", "condition"] = "Intermittent"
    sleep_duration = sleep_duration[sleep_duration.condition != "Intermittent"]
    return sleep_duration.set_index(["animal", "condition"])

def get_pvalues(df, sleep_duration, event_kinds=("spindle",)):
    res_inds = get_stats_ind(df, sleep_duration, event_kinds)

    # Averaging results across Vehicle 1 and Vehicle 2
    res_inds.loc[res_inds.condition == "Vehicle 1", "condition"] = "Vehicle"
    res_inds.loc[res_inds.condition == "Vehicle 2", "condition"] = "Vehicle"
    res_inds = res_inds.groupby(["feature", "condition", "scoring", "animal"]).mean().reset_index()

    p_val_res = []
    for (scoring, feature), df in res_inds.groupby(['scoring', 'feature']):
        for x, y in list(combinations(df.condition.unique(), 2)):
            res = {"scoring": scoring, "feature": feature}
            tmp = df.set_index("animal")  # Aligned by the index when concatenating
            tmp = pd.concat([tmp[tmp.condition == x].rename(columns={"value": x})[x],
                             tmp[tmp.condition == y].rename(columns={"value": y})[y]],
                            axis=1).dropna()
            res["t-value"], res["p-value"] = ttest_rel(tmp[x], tmp[y])
            res["N"] = len(tmp[x])
            res["condition1"] = x
            res["condition2"] = y
            p_val_res.append(res)

    return pd.DataFrame(p_val_res).sort_values(["scoring", "feature"])

def get_stats_ind(df, sleep_duration, event_kinds=("spindle", "spindle_manual")):
    all_res = []
    for scoring in event_kinds:
        for feature in ["duration", "p2p_amp"]:
            tmp = df.groupby(["event", "animal", "condition"]).mean()[[feature]].reset_index()
            tmp = tmp.pivot_table(index=["event", "animal"], columns="condition")[feature]
            if feature == "p2p_amp":
                tmp.loc[tmp[df.condition.unique()[0]] > 5000, df.condition.unique()] /= 1000

            res = tmp.loc[scoring]
            res["scoring"] = scoring
            res["feature"] = feature
            all_res.append(res)

        feature = "duration"
        tmp = df.groupby(["event", "animal", "condition"]).count()[[feature]].reset_index("event")
        tmp = tmp.merge(sleep_duration, left_index=True, right_index=True)
        tmp[feature] /= tmp.nrem_min
        tmp = tmp.drop(columns="nrem_min").rename(columns={"duration": "density"}).reset_index()
        feature = "density"
        tmp = tmp.pivot_table(index=["event", "animal"], columns="condition")[feature]

        res = tmp.loc[scoring]
        res["scoring"] = scoring
        res["feature"] = feature
        all_res.append(res)

    stats_df = pd.concat(all_res).reset_index().melt(id_vars=["animal", "scoring", "feature"])
    return stats_df.sort_values(["scoring", "feature", "condition"])

def get_stats(df, sleep_duration, event_kinds=("spindle", "spindle_manual")):
    res_inds = get_stats_ind(df, sleep_duration, event_kinds)

    # Averaging results across Vehicle 1 and Vehicle 2
    res_inds.loc[res_inds.condition == "Vehicle 1", "condition"] = "Vehicle"
    res_inds.loc[res_inds.condition == "Vehicle 2", "condition"] = "Vehicle"
    res_inds = res_inds.groupby(["feature", "condition", "scoring", "animal"]).mean().reset_index()

    return pd.concat([res_inds.groupby(["feature", "condition", "scoring"]).mean().rename(columns={"value": "mean"})["mean"],
                      res_inds.groupby(["feature", "condition", "scoring"]).std().rename(columns={"value": "std"})["std"],
                      res_inds.groupby(["feature", "condition", "scoring"]).count().rename(columns={"value": "count"})["count"]],
                     axis=1)

def get_spin_feat_df(df_perf, file_pattern="{animal}_{condition}_spin_feat.csv"):
    dfs = []
    for animal, condition in df_perf[["animal", "condition"]].values:
        path = Path(".") / file_pattern.format(animal=animal,
                                               condition=condition)
        if not path.exists():
            continue
        df = pd.read_csv(path, index_col=0)
        df['animal'], df['condition'] = path.name.split("_")[:2]
        dfs.append(df)

    df = pd.concat(dfs)
    #df.loc[df.condition == "Vehicle 1", "condition"] = "Vehicle"
    #df.loc[df.condition == "Vehicle 2", "condition"] = "Vehicle"
    df.loc[df.condition == "Intermittent 1", "condition"] = "Intermittent"
    df.loc[df.condition == "Intermittent 2", "condition"] = "Intermittent"
    return df[df.condition != "Intermittent"]

def get_perf_df(study_no, edf_files, sleep_files,
                spindle_files, recompute=False,
                file_pattern="detection_performance_study{study_no}.csv"):
    file_name = Path(file_pattern.format(study_no=study_no))
    if recompute or not file_name.exists():
        results = []
        for key in tqdm(list(edf_files)):
            raw = mne.io.read_raw_edf(edf_files[key])
            ch_names = raw.pick("eeg").ch_names
            ch_names = [ch_name for ch_name in ch_names
                        if "eeg" in ch_name.lower() or "channel" in ch_name.lower()]
            for ch_name in ch_names:
                raw = get_annotated_raw(edf_files[key], spindle_files[key],
                                        sleep_files[key], ch_name)
                sample_df = get_sample_df(raw, sleep_files[key])
                res = get_performances(sample_df)
                res["animal"] = key[0]
                res["condition"] = key[1]
                res["ch_name"] = ch_name
                results.append(res)

        df_perf = pd.DataFrame(results)
        df_perf = df_perf.sort_values('f1', ascending=False).drop_duplicates(['animal','condition'])
        df_perf = df_perf.reset_index(drop=True).sort_values("animal")
        df_perf.to_csv(file_name)
        return df_perf.drop(columns="ch_name")

    df_perf = pd.read_csv(file_name, index_col=0).reset_index(drop=True)
    return df_perf.sort_values("animal")

def get_whole_raw(edf_file, condition, animal, start_times, ch_name):
    raw = mne.io.read_raw_edf(edf_file)
    tmin = (start_times[(animal, condition)] - start_times[(animal, "whole")]).days*24*60*60
    tmin += (start_times[(animal, condition)] - start_times[(animal, "whole")]).seconds
    tmin +=  (start_times[(animal, condition)] - start_times[(animal, "whole")]).microseconds/10e6
    tmax = tmin + 12*60*60
    cropped_raw = raw.copy().crop(tmin, tmax)
    cropped_raw.set_meas_date(start_times[(animal, condition)])
    cropped_raw._cropped_samp = 0
    return cropped_raw

def get_start_times(whole_edf_files, edf_files):
    start_times = {}
    for animal in tqdm(whole_edf_files):
        for condition in ["baseline", "kynurenine"]:
            raw = mne.io.read_raw_edf(edf_files[(animal, condition)])
            start_times[(animal, condition)] = raw.info["meas_date"]

        raw = mne.io.read_raw_edf(whole_edf_files[animal])
        start_times[(animal, "whole")] = raw.info["meas_date"]

    return start_times


import os
from pathlib import Path

import win32com.client
import openpyxl
from tkinter import *
from tkinter import filedialog

def xls_to_xlsx(folder_path, file_name):
    """
    excel  .xls 后缀 改成 .xlsx 后缀
    folder_path 文件夹路径
    file_name 文件名字 带后缀 比如 aa.xls
    """
    folder_path = folder_path.replace('/', '\\')
    file_name = file_name.replace('/', '\\')
    name, suffix = file_name.split('.')
    excel_file_path = os.path.join(folder_path, file_name)

    excel = win32com.client.gencache.EnsureDispatch('Excel.Application')  # 要看MIME手册
    wb = excel.Workbooks.Open(excel_file_path)
    suffix = f".{suffix}x"
    new_file_name = f"{name}{suffix}"
    new_excel_file_path = os.sep.join([folder_path, new_file_name])
    # test
    print("new_excel_file_path: " + new_excel_file_path)
    wb.SaveAs(new_excel_file_path, FileFormat=51)
    wb.Close()
    excel.Application.Quit()
    return new_excel_file_path

def generate_one_warrant(excel_path, confirm_sheet, index, para_list):
    warrant_book = openpyxl.load_workbook(excel_path, data_only=True)
    warrant_sheet1 = warrant_book["1"]  # 股权证首页
    warrant_sheet2 = warrant_book["2"]  # 股东基本信息登记
    warrant_sheet3 = warrant_book["3"]  # 股权登记
    # warrant_sheet2['E25'] = confirm_sheet['A10'].value

    confirm_row_index = 10
    warrant_sheet2_row_index = 25
    warrant_sheet3_row_index = 24

    sheet_id = confirm_sheet['K2'].value

    count = 0
    while confirm_sheet.cell(confirm_row_index, 1).value is not None \
            and not (('家庭' in confirm_sheet.cell(confirm_row_index, 1).value)
                     and (confirm_sheet.cell(confirm_row_index, 3).value is None)):
        member_name = confirm_sheet.cell(confirm_row_index, 1).value
        id_number = confirm_sheet.cell(confirm_row_index, 5).value
        gender = ''
        if id_number is None or id_number == '':
            print("警告：编号为{}的表中身份证号缺失".format(sheet_id))
        else:
            id_number = id_number.strip()
            if len(id_number) != 18:
                print("警告：编号为{}的表中身份证号位数不为18".format(sheet_id))
            if len(id_number) < 2:
                print("警告：编号为{}的表中身份证号位数小于2".format(sheet_id))
            else:
                if id_number[-2].isdigit():
                    if int(id_number[-2]) % 2 == 0:
                        gender = '女'
                    else:
                        gender = '男'

        warrant_sheet2.cell(warrant_sheet2_row_index, 5).value = member_name  # 姓名
        warrant_sheet2.cell(warrant_sheet2_row_index, 6).value = gender  # 性别
        warrant_sheet2.cell(warrant_sheet2_row_index, 7).value = id_number  # 身份证号码
        warrant_sheet2.cell(warrant_sheet2_row_index, 8).value = confirm_sheet.cell(confirm_row_index, 3).value  # 与户主关系

        warrant_sheet3.cell(warrant_sheet3_row_index, 19).value = member_name
        warrant_sheet3.cell(warrant_sheet3_row_index, 20).value = 10
        warrant_sheet3.cell(warrant_sheet3_row_index, 23).value = '成员股'

        confirm_row_index = confirm_row_index + 1
        warrant_sheet2_row_index = warrant_sheet2_row_index + 1
        warrant_sheet3_row_index = warrant_sheet3_row_index + 1
        count = count + 1
        # '家庭' not in confirm_sheet.cell(row_index, 1).value) and (confirm_sheet.cell(row_index, 3).value is not None

        if count > 14:
            print('警告：编号为{}的股权证中人数超过14，需手动调整！'.format(sheet_id))

    warrant_sheet3['T39'] = count * 10

    last4 = str(index + 1).zfill(4)

    warrant_sheet1['X22'] = para_list[0]
    warrant_sheet1['V31'] = para_list[1]
    warrant_sheet1['W54'] = para_list[2]
    warrant_sheet1['W81'] = para_list[3] + last4
    warrant_sheet1['V89'] = para_list[4]
    warrant_sheet1['X89'] = para_list[5]
    warrant_sheet1['AA89'] = para_list[6]

    warrant_sheet3['T42'] = para_list[7]

    filename_save = last4 + confirm_sheet['A10'].value + '股权证.xlsx'
    (Path("warrant") / para_list[1]).mkdir(parents=True, exist_ok=True)

    folder_path2 = Path("warrant") / para_list[1]
    whole_save = folder_path2 / filename_save
    warrant_book.save(whole_save)
    # print(count)
    warrant_book.close()

def generate_share_warrants(warrant_path, confirm_path, para_list):
    folder_path, file_name = os.path.split(warrant_path)
    # folder_path = r"C:\Users\sc\Desktop\test_openpyxl"
    # file_name = 'sample.xlsx'
    old_suffix = file_name.split('.')[-1]
    if old_suffix == 'xls':
        print("transform .xls to .xlsx")
        excel_path = xls_to_xlsx(folder_path, file_name)
    elif old_suffix == 'xlsx':
        print("no need to transform file type")
        excel_path = os.sep.join([folder_path, file_name])
        # print(excel_path)
    else:
        print("wrong file type: " + old_suffix)
        return

    folder_path2, file_name2 = os.path.split(confirm_path)
    # folder_path = r"C:\Users\sc\Desktop\test_openpyxl"
    # file_name = 'sample.xlsx'
    old_suffix2 = file_name2.split('.')[-1]
    if old_suffix2 == 'xls':
        print("transform .xls to .xlsx")
        excel_path2 = xls_to_xlsx(folder_path2, file_name2)
    elif old_suffix2 == 'xlsx':
        print("no need to transform file type")
        excel_path2 = os.sep.join([folder_path2, file_name2])
        # print(excel_path)
    else:
        print("wrong file type: " + old_suffix2)
        return

    confirm_book = openpyxl.load_workbook(excel_path2, data_only=True)
    confirm_sheet_names = confirm_book.sheetnames

    for i in range(len(confirm_sheet_names)):
        generate_one_warrant(excel_path, confirm_book[confirm_sheet_names[i]], i, para_list)

    if old_suffix == 'xls':
        os.remove(excel_path)

    confirm_book.close()

if __name__ == '__main__':
    path_input = ''

    top = Tk()
    frame_file = Frame(top)

    # 变量path
    path = StringVar()
    # 输入框，标记，按键
    Label(top, text="确认表路径:").pack()
    # 输入框绑定变量path
    Entry(top, textvariable=path, width=30).pack()

    def selectPath():
        global path_input
        path_input = filedialog.askopenfilename(title='请选择确认登记表', filetypes=[('Excel', '.xls .xlsx')])
        path.set(path_input)
        print(path_input)

    Button(top, text="路径选择", command=selectPath).pack()

    # 1
    frame_credit_code = Frame(top)
    label_credit_code = Label(frame_credit_code, text="统一社会信用代码")
    label_credit_code.pack(side=LEFT)
    entry_credit_code = Entry(frame_credit_code)
    entry_credit_code.pack(side=RIGHT)
    frame_credit_code.pack()

    # 2
    frame_organization = Frame(top)
    label_organization = Label(frame_organization, text="组织名称")
    label_organization.pack(side=LEFT)
    entry_organization = Entry(frame_organization)
    entry_organization.pack(side=RIGHT)
    frame_organization.pack()

    # 3
    frame_representative = Frame(top)
    label_representative = Label(frame_representative, text="法定代表人")
    label_representative.pack(side=LEFT)
    entry_representative = Entry(frame_representative)
    entry_representative.pack(side=RIGHT)
    frame_representative.pack()

    # 4
    frame_number = Frame(top)
    label_number = Label(frame_number, text="股权证编号（除了后四位）")
    label_number.pack(side=LEFT)
    entry_number = Entry(frame_number)
    entry_number.pack(side=RIGHT)
    frame_number.pack()

    # 5, 6, 7
    frame_issue_date = Frame(top)
    Label(frame_issue_date, text="发证日期").pack(side=LEFT)
    entry_year = Entry(frame_issue_date, width=5)
    entry_year.pack(side=LEFT)
    Label(frame_issue_date, text="年").pack(side=LEFT)
    entry_month = Entry(frame_issue_date, width=5)
    entry_month.pack(side=LEFT)
    Label(frame_issue_date, text="月").pack(side=LEFT)
    entry_day = Entry(frame_issue_date, width=5)
    entry_day.pack(side=LEFT)
    Label(frame_issue_date, text="日").pack(side=LEFT)

    frame_issue_date.pack()

    # 8
    frame_register_date = Frame(top)
    Label(frame_register_date, text="登记日期").pack(side=LEFT)
    entry_register_date = Entry(frame_register_date)
    entry_register_date.pack(side=LEFT)
    frame_register_date.pack()

    def btn_generator_CallBack():
        para_list = [entry_credit_code.get(),
                     entry_organization.get(),
                     entry_representative.get(),
                     entry_number.get(),
                     entry_year.get(),
                     entry_month.get(),
                     entry_day.get(),
                     entry_register_date.get()]

        # messagebox.showinfo("Hello Python", para_list)

        print(os.getcwd())
        sample_path = os.path.join(os.getcwd(), 'sample')

        global path_input
        generate_share_warrants(os.path.join(sample_path, 'sample.xlsx'), path_input, para_list)

        print('股权证生成完毕。')

    B = Button(top, text="生成股权证", command=btn_generator_CallBack)
    B.pack()
    top.mainloop()


import torch
import torch.nn as nn
import torchvision
import numpy as np
import torch.utils.model_zoo as model_zoo
import torchvision.models as models
print("PyTorch Version: ", torch.__version__)
print("Torchvision Version: ", torchvision.__version__)

# __all__ = ['ResNet50', 'ResNet101', 'ResNet152','ResNet153']
model_urls = {
    'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
    'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
    'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
    'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
    'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
}

def Conv1(in_planes, places, stride=2):
    return nn.Sequential(
        nn.Conv2d(in_channels=in_planes, out_channels=places, kernel_size=7, stride=stride, padding=3, bias=False),
        nn.BatchNorm2d(places),
        nn.ReLU(inplace=True),
        nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
    )

class Bottleneck(nn.Module):
    def __init__(self, in_places, places, stride=1, downsampling=False, expansion=4):
        super(Bottleneck, self).__init__()
        self.expansion = expansion
        self.downsampling = downsampling

        self.bottleneck = nn.Sequential(
            nn.Conv2d(in_channels=in_places, out_channels=places, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(places),
            nn.ReLU(inplace=True),
            nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False),
            nn.BatchNorm2d(places),
            nn.ReLU(inplace=True),
            nn.Conv2d(in_channels=places, out_channels=places * self.expansion, kernel_size=1, stride=1, bias=False),
            nn.BatchNorm2d(places * self.expansion),
        )

        if self.downsampling:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channels=in_places, out_channels=places * self.expansion, kernel_size=1, stride=stride,
                          bias=False),
                nn.BatchNorm2d(places * self.expansion)
            )
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        residual = x
        out = self.bottleneck(x)

        if self.downsampling:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)
        return out

class _PositionAttentionModule(nn.Module):
    """ Position attention module"""

    def __init__(self, in_channels, **kwargs):
        super(_PositionAttentionModule, self).__init__()
        self.conv_b = nn.Conv2d(in_channels, in_channels // 8, 1)
        self.conv_c = nn.Conv2d(in_channels, in_channels // 8, 1)
        self.conv_d = nn.Conv2d(in_channels, in_channels, 1)
        self.alpha = nn.Parameter(torch.zeros(1))
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, x):
        batch_size, _, height, width = x.size()  # [b,512,14,14]
        feat_b = self.conv_b(x).view(batch_size, -1, height * width).permute(0, 2, 1)
        feat_c = self.conv_c(x).view(batch_size, -1, height * width)
        attention_s = self.softmax(torch.bmm(feat_b, feat_c))
        feat_d = self.conv_d(x).view(batch_size, -1, height * width)
        feat_e = torch.bmm(feat_d, attention_s.permute(0, 2, 1)).view(batch_size, -1, height, width)
        out = self.alpha * feat_e + x  # [b,512,14,14]

        return out

class _ChannelAttentionModule(nn.Module):
    """Channel attention module"""

    def __init__(self, **kwargs):
        super(_ChannelAttentionModule, self).__init__()
        self.beta = nn.Parameter(torch.zeros(1))
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, x):
        batch_size, _, height, width = x.size()
        feat_a = x.view(batch_size, -1, height * width)
        feat_a_transpose = x.view(batch_size, -1, height * width).permute(0, 2, 1)
        attention = torch.bmm(feat_a, feat_a_transpose)
        attention_new = torch.max(attention, dim=-1, keepdim=True)[0].expand_as(attention) - attention
        attention = self.softmax(attention_new)

        feat_e = torch.bmm(attention, feat_a).view(batch_size, -1, height, width)
        out = self.beta * feat_e + x  #

        return out

class ResNet(nn.Module):
    def __init__(self, blocks, norm_layer=nn.BatchNorm2d, norm_kwargs=None, num_classes=1000, expansion=4, **kwargs):
        super(ResNet, self).__init__()
        self.expansion = expansion

        self.conv1 = Conv1(in_planes=3, places=64)

        self.layer1 = self.make_layer(in_places=64, places=64, block=blocks[0], stride=1)

        self.layer1_down = nn.Sequential(nn.Conv2d(256, 1024, 1, stride=2, bias=True),
                                         norm_layer(1024, **({} if norm_kwargs is None else norm_kwargs)),
                                         nn.Conv2d(1024, 2048, 1, stride=2, bias=True),
                                         norm_layer(2048, **({} if norm_kwargs is None else norm_kwargs)))

        self.layer2 = self.make_layer(in_places=256, places=128, block=blocks[1], stride=2)

        self.conv_p1 = nn.Sequential(
            nn.Conv2d(512, 512 // 4, 3, padding=1, bias=False),
            norm_layer(512 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.conv_c1 = nn.Sequential(
            nn.Conv2d(512, 512 // 4, 3, padding=1, bias=False),
            norm_layer(512 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.pam = _PositionAttentionModule(512 // 4, **kwargs)
        self.cam = _ChannelAttentionModule(**kwargs)
        self.conv_p2 = nn.Sequential(
            nn.Conv2d(512 // 4, 512 // 4, 3, padding=1, bias=True),
            norm_layer(512 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.conv_c2 = nn.Sequential(
            nn.Conv2d(512 // 4, 512 // 4, 3, padding=1, bias=True),
            norm_layer(512 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True))

        self.fusion_cp1 = nn.Sequential(nn.Conv2d(512 // 4, 512, 1, bias=True),
                                        nn.BatchNorm2d(512))

        self.down = nn.Sequential(nn.Conv2d(512, 1024, 1, stride=2, bias=True), nn.BatchNorm2d(1024))

        self.layer3 = self.make_layer(in_places=512, places=256, block=blocks[2], stride=2)

        self.conv_p1_layer3 = nn.Sequential(
            nn.Conv2d(1024, 1024 // 4, 3, padding=1, bias=False),
            norm_layer(1024 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.conv_c1_layer3 = nn.Sequential(
            nn.Conv2d(1024, 1024 // 4, 3, padding=1, bias=False),
            norm_layer(1024 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.pam_layer3 = _PositionAttentionModule(1024 // 4, **kwargs)
        self.cam_layer3 = _ChannelAttentionModule(**kwargs)
        self.conv_p2_layer3 = nn.Sequential(
            nn.Conv2d(1024 // 4, 1024 // 4, 3, padding=1, bias=True),
            norm_layer(1024 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.conv_c2_layer3 = nn.Sequential(
            nn.Conv2d(1024 // 4, 1024 // 4, 3, padding=1, bias=True),
            norm_layer(1024 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True))

        self.fusion_cp1_layer3 = nn.Sequential(nn.Conv2d(1024 // 4, 1024, 1, bias=True),
                                               nn.BatchNorm2d(1024))

        self.down_layer3 = nn.Sequential(nn.Conv2d(1024, 2048, 1, stride=1, bias=True), nn.BatchNorm2d(2048))

        self.layer4 = self.make_layer(in_places=1024, places=512, block=blocks[3], stride=1)

        self.conv_p1_layer4 = nn.Sequential(
            nn.Conv2d(2048, 2048 // 4, 3, padding=1, bias=False),
            norm_layer(2048 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.conv_c1_layer4 = nn.Sequential(
            nn.Conv2d(2048, 2048 // 4, 3, padding=1, bias=False),
            norm_layer(2048 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.pam_layer4 = _PositionAttentionModule(2048 // 4, **kwargs)
        self.cam_layer4 = _ChannelAttentionModule(**kwargs)
        self.conv_p2_layer4 = nn.Sequential(
            nn.Conv2d(2048 // 4, 2048 // 4, 3, padding=1, bias=True),
            norm_layer(2048 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True)
        )
        self.conv_c2_layer4 = nn.Sequential(
            nn.Conv2d(2048 // 4, 2048 // 4, 3, padding=1, bias=True),
            norm_layer(2048 // 4, **({} if norm_kwargs is None else norm_kwargs)),
            nn.ReLU(True))

        self.fusion_cp1_layer4 = nn.Sequential(nn.Conv2d(2048 // 4, 2048, 1, bias=True),
                                               nn.BatchNorm2d(2048))
        self.avgpool = nn.AvgPool2d(14, stride=1)

        self.fc = nn.Linear(2048,num_classes)

        # for m in self.modules():
        #     if isinstance(m, nn.Conv2d):
        #         nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
        #     elif isinstance(m, nn.BatchNorm2d):
        #         nn.init.constant_(m.weight, 1)
        #         nn.init.constant_(m.bias, 0)

    def make_layer(self, in_places, places, block, stride):
        layers = []
        layers.append(Bottleneck(in_places, places, stride, downsampling=True))
        for i in range(1, block):
            layers.append(Bottleneck(places * self.expansion, places))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)

        x = self.layer1(x)  # [b,256,56,56]
        x_layer1 = self.layer1_down(x)  # torch.Size([1, 2048, 14, 14])
        layer2 = self.layer2(x)  # [b,512,28,28]

        feat_p = self.conv_p1(layer2)  # torch.Size([1, 128, 28, 28])
        feat_p = self.pam(feat_p)  # torch.Size([1, 128, 28, 28])
        feat_p = self.conv_p2(feat_p)  # torch.Size([1, 128, 28, 28])

        feat_c = self.conv_c1(layer2)  # torch.Size([1, 128, 28, 28])

        feat_c = self.cam(feat_c)  # torch.Size([1, 128, 28, 28])

        feat_c = self.conv_c2(feat_c)  # torch.Size([1, 128, 28, 28])

        feat_fusion_layer1 = feat_p + feat_c  # torch.Size([1, 128, 28, 28])
        x = self.fusion_cp1(feat_fusion_layer1)  # torch.Size([1, 512, 28, 28])

        # print("new_F2生成完毕")

        x_downsample = self.down(x)  # torch.Size([1, 1024, 14, 14])

        layer3 = self.layer3(x)  # torch.Size([1, 1024, 14, 14])
        layer3 = x_downsample + layer3
        feat_p_layer3 = self.conv_p1_layer3(layer3)
        feat_p_layer3 = self.pam_layer3(feat_p_layer3)
        feat_p_layer3 = self.conv_p2_layer3(feat_p_layer3)

        feat_c_layer3 = self.conv_c1_layer3(layer3)

        feat_c_layer3 = self.cam_layer3(feat_c_layer3)

        feat_c_layer3 = self.conv_c2_layer3(feat_c_layer3)
        feat_fusion_layer3 = feat_p_layer3 + feat_c_layer3
        x = self.fusion_cp1_layer3(feat_fusion_layer3)  # torch.Size([1, 1024, 14, 14])

        # print("new_F3生成完毕")

        layer4 = self.layer4(x) + self.down_layer3(x)

        feat_p_layer4 = self.conv_p1_layer4(layer4)
        feat_p_layer4 = self.pam_layer4(feat_p_layer4)
        feat_p_layer4 = self.conv_p2_layer4(feat_p_layer4)

        feat_c_layer4 = self.conv_c1_layer4(layer4)

        feat_c_layer4 = self.cam_layer4(feat_c_layer4)

        feat_c_layer4 = self.conv_c2_layer4(feat_c_layer4)
        feat_fusion_layer4 = feat_p_layer4 + feat_c_layer4
        x = self.fusion_cp1_layer4(feat_fusion_layer4)  # torch.Size([1, 2048, 14, 14])
        x = x_layer1 + x
        # print("new_F4生成完毕")

        x = self.avgpool(x)
        x = x.view(x.size(0), -1)

        x = self.fc(x)
        return x

def ResNet50():
    return ResNet([3, 4, 6, 3])

def ResNet101():
    return ResNet([3, 4, 23, 3])

def ResNet153():
    ResNet153 = models.resnet152(pretrained=True)
    return ResNet153

def ResNet152():

    resNet152 =models.resnet152(pretrained=True)
    pretrained_dict = resNet152.state_dict()

    model = ResNet([3, 8, 36, 3])
    model_dict = model.state_dict()

    pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
    # 更新现有的model_dict
    model_dict.update(pretrained_dict)

    # 加载真正需要的state_dict
    model.load_state_dict(model_dict)
    return model

if __name__ == '__main__':
    # model = torchvision.models.resnet50()
    model = ResNet152()
    #  print(model)

    input = torch.randn(1, 3, 224, 224)
    out = model(input)
    print(out.shape)


def clearBoard(board):
    for i in range(len(board)):
        newRow = [(num, False) for num, _ in board[i]]
        board[i] = newRow

def markBoard(board, chosenNum):
    for i in range(len(board)):
        newRow = [(num, True if num == chosenNum else isChosen ) for num, isChosen in board[i]]
        board[i] = newRow

def checkRow(board, rowIndex):
    return sum([int(isChosen) for num, isChosen in board[rowIndex]]) == 5

def checkCol(board, colIndex):
    return sum([int(row[colIndex][1]) for row in board]) == 5

def checkBoard(board):
    for i in range(5):
        if checkRow(board, i) or checkCol(board, i):
            return True
    return False

def getSumUnmarked(board):
    return sum([sum([num for num, isMarked in row if not isMarked]) for row in board])

if __name__ == "__main__":
    with open("input", "r") as fileHandle:

        lines = [line for line in fileHandle.read().strip().split('\n') if not line.isspace() and line != ""]

        """lines = [line for line in
'''
7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1

22 13 17 11  0
 8  2 23  4 24
21  9 14 16  7
 6 10  3 18  5
 1 12 20 15 19

 3 15  0  2 22
 9 18 13 17  5
19  8  7 25 23
20 11 10 24  4
14 21 16 12  6

14 21 17 24  4
10 16 15  9 19
18  8 23 26 20
22 11 13  6  5
 2  0 12  3  7'''.strip().split('\n') if not line.isspace() and line != ""]"""

        bingoNumbers = list(map(lambda x: int(x), lines[0].split(",")))
        bingoBoards = []
        for i in range(1, len(lines), 5):
            bingoBoard = list(map(lambda x: [(int(y), False) for y in x.split()], lines[i:(i+5)]))
            bingoBoards.append(bingoBoard)

    # Part 1
    winningBoard = None
    winningNumber = None

    for num in bingoNumbers:
        for board in bingoBoards:
            markBoard(board, num)

            if checkBoard(board):
                winningBoard = board
                winningNumber = num
                break
        if winningBoard is not None:
            break

    if winningBoard is not None:
        sumUnmarked = getSumUnmarked(winningBoard)
        key = sumUnmarked * winningNumber
        print("Part 1:", key)

    for board in bingoBoards:
        clearBoard(board)

    winningBoards = []
    losingBoard = None
    losingNumber = None

    for num in bingoNumbers:
        winningBoardsThisRound = []
        for board in bingoBoards:
            markBoard(board, num)

            if board not in winningBoards and checkBoard(board):
                winningBoards.append(board)

                if len(winningBoards) == len(bingoBoards):  # This was the last board to win.
                    losingBoard = board
                    losingNumber = num
                    break
        if losingBoard is not None:
            break

    if losingBoard is not None:
        sumUnmarked = getSumUnmarked(losingBoard)
        key = sumUnmarked * losingNumber
        print("Part 2:", key)


# -*- coding: utf-8 -*-
import os, re, time
from pyautogui import prompt

from sys import path
path.append(r'..\..\_comum')
from comum_comum import _time_execution, _open_lista_dados

def cria_txt(empresa, tipo, encode='latin-1'):
    info1, info2, info3, info4, info5, info6, info7, codigo, cnpj = empresa

    local = os.path.join('Execução', str(codigo) + '-' + str(cnpj))
    os.makedirs(local, exist_ok=True)

    try:
        f = open(os.path.join(local, f"{tipo} - {cnpj}.txt"), 'a', encoding=encode)
    except:
        f = open(os.path.join(local, f"{tipo}  - {cnpj} - auxiliar.txt"), 'a', encoding=encode)

    f.write(str(';'.join([info1, info2, info3, info4, info5, info6, info7, codigo])) + '\n')
    f.close()

@_time_execution
def run():
    tipo = prompt(title='Script incrível', text='Qual será o nome base do arquivo ".txt"?')
    # função para abrir a lista de dados
    empresas = _open_lista_dados()

    for empresa in empresas:
        cria_txt(empresa, tipo)

if __name__ == '__main__':
    run()

Geral/Analisa EXCEL e separa em txt/analisa_excel_separa_em_txt.py


import math

def prime(n):
    if (math.factorial(n-1) + 1) % n != 0:
        #print("Это составное число")
        return 0
    else:
        #print("Это простое число")
        return 1

def is_palindrome(word):
   for i in range(len(word)//2):
         if word[i] != word[-1-i]:
                 return False
   return True

#print (str(10000*10000)+"-"+str (99999*99999))

def func (a):
    flag=False
    b=99999
    while ( flag==False and b>10000):
        if (prime(b)==1):
            print(str(b)+" "+str(a%b))
            if ((a%b)==0):
                c=a//b
                if ((prime(c))==1):
                    print(str(c)+" result  "+str(b))
                    return 1
            if (b<(a/b)):
                flag=True
                return 0
        b=b-1

mn1=1
mn2=1
a=9605225069
while (a>1000000000):
    if (is_palindrome(str(a))==True):
        print(str(a))
        if (func(a)==1):
            a=1
    a=a-1


# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html
import boto3
from botocore.exceptions import ClientError
from dotenv import load_dotenv
import logging
import os
import glob

load_dotenv()

BUCKET_NAME = "asdi-hackathon-data"
REGION = "us-west-1"

# Using default profile (attached role)
'''
session = boto3.Session(region_name = REGION)
client = session.client('s3')
'''

#
client = boto3.client(
    's3',
    aws_access_key_id=os.environ.get("AWS_ACCESS_KEY"),
    aws_secret_access_key=os.environ.get("AWS_SECRET_KEY"),
    region_name=REGION
)

def upload_file(file_name, object_name):
    try:
        response = client.upload_file(file_name, BUCKET_NAME, object_name)
        if(response):print("Success")
    except ClientError as e:
        logging.error(e)
        return False
    return True

def upload_json_files(folder_path):

    file_names = map(os.path.basename, glob.glob(folder_path))
    for file_name in file_names:
        src = f"json/{file_name}"
        dest = f"json/{file_name}"
        upload_file(src, dest)

upload_json_files("./json/*")


#Next, implement the function getGuessedWord that takes in two parameters - a string, secretWord,
#and a list of letters, lettersGuessed. This function returns a string that is comprised of letters
#and underscores, based on what letters in lettersGuessed are in secretWord.
# This shouldn't be too different from isWordGuessed!
#
#Example Usage:
#
#>>> secretWord = 'apple'
#>>> lettersGuessed = ['e', 'i', 'k', 'p', 'r', 's']
#>>> print(getGuessedWord(secretWord, lettersGuessed))
#'_ pp_ e'
def getGuessedWord(secretWord, lettersGuessed):
    '''
    secretWord: string, the word the user is guessing
    lettersGuessed: list, what letters have been guessed so far
    returns: string, comprised of letters and underscores that represents
      what letters in secretWord have been guessed so far.
    '''
    s=""
    for char in secretWord:
        if char in lettersGuessed:
            s+=char
        else:
            s+=" _ "
    return s


# You are playing the following Bulls and Cows game with your friend:
# You write down a number and ask your friend to guess what the number is.
# Each time your friend makes a guess,
# you provide a hint that indicates how many digits in said guess match your secret number exactly in both digit and position
# (called "bulls") and how many digits match the secret number but locate in the wrong position (called "cows").
# Your friend will use successive guesses and hints to eventually derive the secret number.
# Write a function to return a hint according to the secret number and friend's guess,
# use A to indicate the bulls and B to indicate the cows.
# Please note that both secret number and friend's guess may contain duplicate digits.

# Example 1:
# Input: secret = "1807", guess = "7810"
# Output: "1A3B"
# Explanation: 1 bull and 3 cows. The bull is 8, the cows are 0, 1 and 7.

# Example 2:
# Input: secret = "1123", guess = "0111"
# Output: "1A1B"
# Explanation: The 1st 1 in friend's guess is a bull, the 2nd or 3rd 1 is a cow.

# Note: You may assume that the secret number and your friend's guess only contain digits,
# and their lengths are always equal.

class Solution(object):
    def getHint(self, secret, guess):
        """
        :type secret: str
        :type guess: str
        :rtype: str
        """
        hint = ""
        m = len(secret)

        bulls = 0
        cows = 0
        secret_dict = {}
        guess_dict = {}
        for i in range(m):
            if secret[i] == guess[i]:
                bulls += 1
            else:
                if secret[i] not in secret_dict:
                    secret_dict[secret[i]] = 1
                else:
                    secret_dict[secret[i]] += 1
                if guess[i] not in guess_dict:
                    guess_dict[guess[i]] = 1
                else:
                    guess_dict[guess[i]] += 1

        for key in secret_dict:
            if key in guess_dict:
                cows += min(guess_dict[key], secret_dict[key])

        hint += str(bulls) + "A" + str(cows) + "B"
        return hint

LeetCode/Python3/String/299. Bulls and Cows.py


import os
import logging
import shutil
import sys
import threading
import time

from configparser import ConfigParser

logging.basicConfig(level=logging.INFO)

class UbuntuFiles(object):
    '''
    需求：
    在Ubuntu下通过这个文件可以批量复制和删除
    '''
    thread_list = []

    def __new__(cls):
        cls.files()
        return super().__new__(cls)

    def __init__(self):
        self.rm_files = dict(self.section)['rm_files'].split(',')
        self.cp_files = dict(self.section)['cp_files'].split(',')
        self.cp_or_rm_files_path = dict(self.section)['cp_or_rm_files_path']

    def copy_files(self, file):
        # 复制文件
        if self.cp_or_rm_files_path and file:
            f = './' + file
            cp_file = self.cp_or_rm_files_path + file
            if not os.path.exists(cp_file):
                try:
                    shutil.copy(f, cp_file)
                    logging.info('复制成功！！！')
                except shutil.SameFileError as identifier:
                    logging.error(identifier)
                    return
                except FileNotFoundError as identifier:
                    logging.error(identifier)
                    return
            else:
                logging.warning('文件已存在！！！')
                return
        else:
            logging.error("请先配置文件！！！")
            return

    def remove_files(self, file):
        # 删除文件
        if self.cp_or_rm_files_path and file:
            f = self.cp_or_rm_files_path + file
            if os.path.exists(f):
                os.unlink(f)
                logging.info('删除成功！！')
            else:
                logging.warning('文件不存在！！！')
                return
        else:
            logging.error("请先配置文件！！！")
            return

    @classmethod
    def files(cls):
        if os.path.exists('./files.conf'):
            cf = ConfigParser()
            cf.read('files.conf')
            section = cf.sections()[0]
            cls.section = cf.items(section)
            return cls
        else:
            logging.error('配置文件不存在！！！')
            return cls

    def run(self):
        # 函数执行
        if len(sys.argv) == 2:
            if sys.argv[1] == 'cp':
                for file in self.cp_files:
                    if file == '*':
                        # TODO:复制该文件夹下所有的文件
                        pass
                    else:
                        t = threading.Thread(target=self.copy_files, args=(file,))
                        self.thread_list.append(t)
            elif sys.argv[1] == 'rm':
                for file in self.rm_files:
                    t = threading.Thread(target=self.remove_files, args=(file,))
                    self.thread_list.append(t)
            else:
                logging.error("请输入合适的参数！！！")
            # 执行线程
            for t in self.thread_list:
                t.setDaemon(True)
                t.start()
            for t in self.thread_list:
                t.join()
            logging.info("线程结束，操作完成！！！")
        elif len(sys.argv) == 1:
            logging.warning("请选择需要的操作！！！")
        else:
            logging.error("请输入合适的参数！！！")

if __name__ == "__main__":
    files = UbuntuFiles()
    files.run()


#Binary Search using python

def Binary_Search(Test_list, item):

	first = 0
	last = len(Test_list)-1
	found = False

	while first <= last and not found:
		mid = (first+last)//2
		if Test_list[mid] == item:
			found = True
		else:
			if Test_list[mid] > item:
				last = mid-1
			else:
				first = mid+1
	return found

Test_list = [1,5,6,7,8,9,10,11,24,26]
Search_item = 26

print(Binary_Search(Test_list, Search_item))


class NumMatrix:  # best

    def __init__(self, matrix: List[List[int]]):  # O(m) per query, O(mn) for precomputer. m -- rows, n -- cols. O(mn) space
        """
        dp is a matrix where rows are the running sum of the rows in matrix
        first column is always the same, cumulative sum starts with the second element
        """
        self.matrix = matrix
        self.dp = [[0] * len(self.matrix[0]) for _ in range(len(self.matrix))]
        for row in range(len(self.matrix)):
            self.dp[row][0] = self.matrix[row][0]
        for row in range(len(self.matrix)):
            for col in range(1, len(self.matrix[0])):
                self.dp[row][col] = self.dp[row][col - 1] + self.matrix[row][col]

    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:  # O(m) time to run
        """
        formula for every row: dp[right] - dp[left] + matrix[left]
        need a for loop to cover the required rows
        """
        self.res = 0
        for row in range(row1, row2 + 1):
            self.res += self.running[row][col2] - self.running[row][col1] + self.matrix[row][col1]
        return self.res

class NumMatrix:
    def __init__(self, matrix):
        if not matrix or not matrix[0]:
            return
        rl, cl = len(matrix), len(matrix[0])
        self.m = [[0 for _ in range(cl + 1)] for _ in range(rl + 1)]
        for i in range(1, rl + 1):
            for j in range(1, cl + 1):
                self.m[i][j] = matrix[i - 1][j - 1] + self.m[i - 1][j] + self.m[i][j - 1] - self.m[i - 1][j - 1]

    def sumRegion(self, row1, col1, row2, col2):
        row1, col1, row2, col2 = row1 + 1, col1 + 1, row2 + 1, col2 + 1
        return self.m[row2][col2] - self.m[row2][col1 - 1] - self.m[row1 - 1][col2] + self.m[row1 - 1][col1 - 1]

class NumMatrix:  # intersections.
    """
    what we are looking is everything minus the rectangle above minus the rectangle to the left
    """

    def __init__(self, matrix):  # O(1) time per query, O(mn) precompute. O(mn) space
        if not matrix or not matrix[0]: return None
        row = len(matrix)
        col = len(matrix[0])
        self.dp = [[0]*(col + 1) for _ in range(row + 1)]
        for i in range(1, row + 1):
            for j in range(1, col + 1):
                self.dp[i][j] = self.dp[i][j - 1] + self.dp[i - 1][j] - self.dp[i - 1][j - 1] + matrix[i - 1][j - 1]

    def sumRegion(self, row1, col1, row2, col2):  # O(1) to run
        return self.dp[row2 + 1][col2 + 1] - self.dp[row2 + 1][col1] - self.dp[row1][col2 + 1] + self.dp[row1][col1]

def test(matrix, row1, col1, row2, col2):
    dp = [[0] * len(matrix[0]) for _ in range(len(matrix))]
    for row in range(len(matrix)):
        dp[row][0] = matrix[row][0]
    for row in range(len(matrix)):
        for col in range(1, len(matrix[0])):
            dp[row][col] = dp[row][col - 1] + matrix[row][col]
    # print(dp)
    res = 0
    # print(matrix)
    # print(dp)
    for i in range(0, row2 - row1 + 1):
        res += dp[row1 + i][col2] - dp[row1 + i][col1] + matrix[row1 + i][col1]
        # print(res, matrix[row1 + i][col1] )
        # res += matrix[row1 + i][col1]

    return res
    # pass
mtx =  [
  [3, 0, 1, 4, 2],
  [5, 6, 3, 2, 1],
  [1, 2, 0, 1, 5],
  [4, 1, 0, 1, 7],
  [1, 0, 3, 0, 5]
]
if __name__ == '__main__':
    print(test(mtx, 2, 1, 4, 3))
    print(test(mtx, 1, 1, 2, 2))
    print(test(mtx, 1, 2, 2, 4))


from decouple import config
import requests
import sys
import json

def send_slack_message(message):
    url = config("SLACK_URL")
    title = f"Decoy Alert"
    slack_data = {
        "username": "Decoy",
        "attachments": [
            {
                "color": "#000000",
                "fields": [
                    {
                        "title": title,
                        "value": message,
                        "short": "false",
                    }
                ]
            }
        ]
    }
    byte_length = str(sys.getsizeof(slack_data))
    headers = {'Content-Type': "application/json", 'Content-Length': byte_length}
    response = requests.post(url, data=json.dumps(slack_data), headers=headers)
    if response.status_code != 200:
        raise Exception(response.status_code, response.text)


import logging
import os
from datetime import datetime

class Logger:
    def __init__(self, logs_path='./models/logs', script_name=os.path.basename(__name__), level='INFO'):
        self.logs_path = logs_path
        self.script_name = script_name
        self.level = level

    def get_level(self):
        lower_level = self.level.lower()
        if lower_level.startswith('critical'):
            l = logging.CRITICAL
        elif lower_level.startswith('error'):
            l = logging.ERROR
        elif lower_level.startswith('warning'):
            l = logging.WARNING
        elif lower_level.startswith('info'):
            l = logging.INFO
        elif lower_level.startswith('debug'):
            l = logging.DEBUG
        else:
            l = logging.INFO

        return l

    def create_logger(self):
        if not os.path.exists(self.logs_path):
            os.makedirs(self.logs_path)

        datetime_for_name = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
        file_name = os.path.join(self.logs_path, f'{self.script_name}_{datetime_for_name}.log')

        self.logger = logging.getLogger(self.script_name)

        while len(self.logger.handlers) > 0:
            h = self.logger.handlers[0]
            self.logger.removeHandler(h)

        l = self.get_level()

        formatter = logging.Formatter('%(asctime)s - %(funcName)s - %(levelname)s - %(message)s')
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        self.logger.addHandler(ch)
        fh = logging.FileHandler(file_name)
        fh.setFormatter(formatter)
        self.logger.addHandler(fh)
        self.logger.setLevel(l)
        self.logger.debug('Log created properly.')

    def update_level(self, level):
        self.level = level
        l = self.get_level()
        self.logger.setLevel(l)

    def shutdown_logger(self):
        self.logger.debug('Logger shutdown.')
        logging.shutdown()


from django.db import models

class Visitor(models.Model):
    visitorid = models.IntegerField(db_column='VisitorId', primary_key=True, verbose_name='№')
    login = models.CharField(db_column='Login', max_length=30, blank=True, null=True, verbose_name='Логин')
    passwordhash = models.CharField(db_column='PasswordHash', max_length=64, blank=True, null=True, verbose_name='Хеш пароля')
    session = models.CharField(db_column='Session', max_length=32, blank=True, null=True, verbose_name='Сессионный ключ')
    lastname = models.CharField(db_column='Lastname', max_length=30, blank=True, null=True, verbose_name='Фамилия')
    firstname = models.CharField(db_column='Firstname', max_length=30, blank=True, null=True, verbose_name='Имя')
    patronymic = models.CharField(db_column='Patronymic', max_length=30, blank=True, null=True, verbose_name='Отчество')
    phonenumber = models.CharField(db_column='PhoneNumber', max_length=19, blank=True, null=True, verbose_name='Номер телефона')
    citizenship = models.CharField(db_column='Citizenship', max_length=30, blank=True, null=True, verbose_name='Гражданство')
    passport = models.CharField(db_column='Passport', max_length=15, blank=True, null=True, verbose_name='Серия и номер паспорта')

    class Meta:
        managed = True
        db_table = 'visitor'
        verbose_name = 'Посетитель'
        verbose_name_plural = 'Посетители'

    def __str__(self):
        return self.login

class Roomclass(models.Model):
    roomclassid = models.AutoField(db_column='RoomClassId', primary_key=True, verbose_name='№')
    name = models.CharField(db_column='Name', max_length=30, blank=True, null=True, verbose_name='Название класса')
    cost = models.PositiveIntegerField(db_column='Cost', blank=True, null=True, verbose_name='Стоимость')
    numberofpeople = models.PositiveIntegerField(db_column='NumberOfPeople', blank=True, null=True, verbose_name='Вместимость')
    comment = models.CharField(db_column='Comment', max_length=200, blank=True, null=True, verbose_name='Описание')

    class Meta:
        managed = True
        db_table = 'roomclass'
        verbose_name = 'Класс номера'
        verbose_name_plural = 'Классы номеров'

    def __str__(self):
        return self.name

class Room(models.Model):
    roomid = models.AutoField(db_column='RoomId', primary_key=True, verbose_name='№')
    roomclassid = models.ForeignKey('Roomclass', models.DO_NOTHING, db_column='RoomClassId', blank=True, null=True, verbose_name='Класс номера')
    roomnumber = models.PositiveIntegerField(db_column='RoomNumber', blank=True, null=True, verbose_name='Номер комнаты')
    avaliable = models.BooleanField(db_column='Avaliable', blank=True, null=True, verbose_name='Активный')

    class Meta:
        managed = True
        db_table = 'room'
        verbose_name = 'Номер'
        verbose_name_plural = 'Номера'

    def __str__(self):
        return f"{self.roomnumber} ({self.roomclassid})"

class Orderinfo(models.Model):
    orderid = models.AutoField(db_column='OrderId', primary_key=True, verbose_name='№')
    visitorid = models.ForeignKey('Visitor', models.DO_NOTHING, db_column='VisitorId', blank=True, null=True, verbose_name='Посетитель')
    roomid = models.ForeignKey('Room', models.DO_NOTHING, db_column='RoomId', blank=True, null=True, verbose_name='Комната')
    checkindate = models.DateField(db_column='CheckInDate', blank=True, null=True, verbose_name='Дата заселения')
    checkoutdate = models.DateField(db_column='CheckOutDate', blank=True, null=True, verbose_name='Дата выселения')
    numberofguests = models.PositiveIntegerField(db_column='NumberOfGuests', blank=True, null=True, verbose_name='Количество гостей')
    cost = models.PositiveIntegerField(db_column='Cost', blank=True, null=True, verbose_name='Стоимость')

    class Meta:
        managed = True
        db_table = 'orderinfo'
        verbose_name = 'Заказ'
        verbose_name_plural = 'Заказы'

    def __str__(self):
        return f"Заказ №{self.orderid}"

class Foodtype(models.Model):
    foodtypeid = models.AutoField(db_column='FoodTypeId', primary_key=True, verbose_name='№')
    name = models.CharField(db_column='Name', max_length=30, blank=True, null=True, verbose_name='Название')
    cost = models.PositiveIntegerField(db_column='Cost', blank=True, null=True, verbose_name='Стоимость')
    avaliable = models.BooleanField(db_column='Avaliable', blank=True, null=True, verbose_name='Активный')
    comment = models.CharField(db_column='Comment', max_length=200, blank=True, null=True, verbose_name='Описание')

    class Meta:
        managed = True
        db_table = 'foodtype'
        verbose_name = 'Тип питания'
        verbose_name_plural = 'Типы питания'

    def __str__(self):
        return self.name

class Food(models.Model):
    foodid = models.AutoField(db_column='FoodId', primary_key=True, verbose_name='№')
    orderid = models.ForeignKey('Orderinfo', models.DO_NOTHING, db_column='OrderId', blank=True, null=True, verbose_name='Заказ')
    foodtypeid = models.ForeignKey('Foodtype', models.DO_NOTHING, db_column='FoodTypeId', blank=True, null=True, verbose_name='Тип питания')
    included = models.BooleanField(db_column='Included', blank=True, null=True, verbose_name='Включено в заказ')

    class Meta:
        managed = True
        db_table = 'food'
        verbose_name = 'Питание'
        verbose_name_plural = 'Питание'

class Paymenttype(models.Model):
    paymenttypeid = models.AutoField(db_column='PaymentTypeId', primary_key=True, verbose_name='№')
    name = models.CharField(db_column='Name', max_length=30, blank=True, null=True, verbose_name='Название')
    avaliable = models.BooleanField(db_column='Avaliable', blank=True, null=True, verbose_name='Активный')

    class Meta:
        managed = True
        db_table = 'paymenttype'
        verbose_name = 'Тип оплаты'
        verbose_name_plural = 'Типы оплаты'

    def __str__(self):
        return self.name

class Orderstatus(models.Model):
    orderstatusid = models.AutoField(db_column='OrderStatusId', primary_key=True, verbose_name='№')
    orderid = models.ForeignKey(Orderinfo, models.DO_NOTHING, db_column='OrderId', blank=True, null=True, verbose_name='Заказ')
    paymenttypeid = models.ForeignKey('Paymenttype', models.DO_NOTHING, db_column='PaymentTypeId', blank=True, null=True, verbose_name='Тип оплаты')
    orderactive = models.BooleanField(db_column='OrderActive', blank=True, null=True, verbose_name='Активный')
    orderpayed = models.BooleanField(db_column='OrderPayed', blank=True, null=True, verbose_name='Оплаченный')

    class Meta:
        managed = True
        db_table = 'orderstatus'
        verbose_name = 'Статус заказа'
        verbose_name_plural = 'Статусы заказов'

    def __str__(self):
        return f"Статус заказа №{self.orderid}"

class Addservicetype(models.Model):
    addservicetypeid = models.AutoField(db_column='AddServiceTypeId', primary_key=True, verbose_name='№')
    name = models.CharField(db_column='Name', max_length=30, blank=True, null=True, verbose_name='Название')
    cost = models.PositiveIntegerField(db_column='Cost', blank=True, null=True, verbose_name='Стоимость')
    avaliable = models.BooleanField(db_column='Avaliable', blank=True, null=True, verbose_name='Активный')

    class Meta:
        managed = True
        db_table = 'addservicetype'
        verbose_name = 'Набор доп. услуг'
        verbose_name_plural = 'Наборы доп. услуг'

    def __str__(self):
        return self.name

class Addservices(models.Model):
    addservicesid = models.AutoField(db_column='AddServicesId', primary_key=True, verbose_name='№')
    orderid = models.ForeignKey('Orderinfo', models.DO_NOTHING, db_column='OrderId', blank=True, null=True, verbose_name='Заказ')
    addservicetypeid = models.ForeignKey('Addservicetype', models.DO_NOTHING, db_column='AddServiceTypeId', blank=True, null=True, verbose_name='Тип доп. услуги')

    class Meta:
        managed = True
        db_table = 'addservices'
        verbose_name = 'Доп. услуга'
        verbose_name_plural = 'Доп. услуги'


from dataset import ImageClassificationDataset
import torchvision.transforms as transforms

class Train_model():
  def __init__(self, directory, CATEGORIES):
   self.directory = directory
   self.categories = CATEGORIES
   self.TRANSFORMS = transforms.Compose([
       transforms.ColorJitter(0.2,0.2,0.2,0.2),
       transforms.Resize((224,224)),
       transforms.ToTensor(),
       transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
   ])
   self.dataset = ImageClassificationDataset(self.directory, self.categories, self.TRANSFORMS)


# flask imports
from flask import Flask, request, jsonify, render_template, redirect, url_for, make_response
from flask_cors import CORS, cross_origin

# general imports
import json
import requests
from datetime import datetime
import os

# speech imports
import azure.cognitiveservices.speech as speechsdk

# recommender imports
import numpy as np
import pandas as pd
from RecommenderSystem import *
from sklearn.metrics.pairwise import pairwise_distances
import torch

##################################################################################################################################################
# load secrets
with open("secrets.json", "r") as f:
    secrets = json.load(f)

# load keys and urls
getEmbeddingUrl = secrets['azure_albert']
watcher_url = secrets['watcher_url']
watcher_auth_token = secrets["watcher_auth_token"]
MIDDLEWARE_KEY = secrets['MIDDLEWARE_KEY']

# config speech2text
speech_config = speechsdk.SpeechConfig(
    subscription=secrets["S2T_KEY"], region=secrets["S2T_REGION"])
speech_config.speech_recognition_language = "en-US"

# start app
app = Flask(__name__)
CORS(app)

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

# load data for recommender
descriptions = np.array(torch.load(
    "data/avgpooled_albert_sentence_embeddings_float32.pt", map_location=torch.device("cpu")))
description_matrix = np.array(torch.load(
    "data/pca_albert_description.pt", map_location=torch.device("cpu")))
title_matrix = np.array(torch.load(
    "data/pca_albert_title.pt", map_location=torch.device("cpu")))
cast_matrix = np.array(torch.load(
    "data/pca_albert_cast.pt", map_location=torch.device("cpu")))
# df = pd.read_csv("data/data_tmbd_cleaned.csv", delimiter=";", lineterminator="\n")
df = pd.read_pickle("data/df.pkl")
df = df[df.popularity >= list(df.popularity.sort_values(ascending=False))[10000]]

# filter matrices
description_matrix = description_matrix[list(df.index),:]
descriptions = descriptions[list(df.index),:]
title_matrix = title_matrix[list(df.index),:]
cast_matrix = cast_matrix[list(df.index),:]

# build matrices
movie_info = ContentBasedRecommender.build_movie_info_matrix(df)
mapping_matrix = ContentBasedRecommender.build_mapping_matrix(df)

# build input
matrix_dict = {
    'movie_info_matrix': movie_info,
    'description_matrix': descriptions,
    'title_matrix': title_matrix,
    'cast_info_matrix': cast_matrix
}

# build recommender system
RS = ContentBasedRecommender(
    mapping_matrix=mapping_matrix,
    **matrix_dict,
    scaling_kwargs=None
)

# define function for checking balance
def check_balance():
    # send request
    response = requests.get(watcher_url + "?key=" + watcher_auth_token)
    # parse response
    if "true" in response.text:
        return True
    else:
        return False

# prepare dataframe and function for drawing samples
# define top genres
top_genres = ['Adventure', 'Animation', 'Fantasy', 'Action',
              'Sci-Fi', 'Horror', 'Thriller', 'Romance', 'Drama', 'Comedy']

# get most rated movies
top_movies = df.sort_values(by='vote_count', ascending=False).head(1000)

def draw_samples(top_movies: pd.DataFrame, top_genres: list) -> pd.DataFrame:
    '''
    This function draws 15 samples from the top 1000 most-rated movies. Ten samples are drawn from the most popular genres, five more at random.

    Args:
    -----
        top_movies:pd.DataFrame - A pandas dataframe containing the top 1000 most-rated movies
        top_genres:list - List containing all genres to select from

    Returns:
    --------
        random_samples:pd.DataFrame - A pandas dataframe containing fifteen samples

    '''
    # prepare dataframe for samples
    random_samples = pd.DataFrame(columns=top_movies.columns)

    # loop over genres to draw samples
    for genre in top_genres:
        genre_movies = top_movies[top_movies[genre] == 1]
        drawn_sample = genre_movies.sample(n=1)

        # check if sample not already in samples
        while random_samples[random_samples.title == drawn_sample.title.values[0]].shape[0] != 0:
            drawn_sample = genre_movies.sample(n=1)

        random_samples = pd.concat((random_samples, drawn_sample))

    # add five more random samples
    random_samples = pd.concat((random_samples, top_movies.sample(n=5)))

    # postprocess titles
    random_samples.title = random_samples.title.apply(
        lambda x: "The " + x.rstrip(", The") if x.endswith(", The") else x)
    random_samples.title = random_samples.title.apply(
        lambda x: "A " + x.rstrip(", A") if x.endswith(", The") else x)

    return random_samples

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

@app.route('/')
@cross_origin()
def index():
    return '''
Middleware works fine
'''

@app.route("/getRecommendationsByIds", methods=['POST'])
@cross_origin()
def getRecommendationsByIds():
    # get data
    data = request.json

    if not check_balance():
        return {"message": "Cost limit reached"}, 403

    # check key
    if data['key'] != MIDDLEWARE_KEY:
        return {"message": "Forbidden Request"}, 403

    ids = data['ids']
    if type(ids) != list:
        return {"message": "Forbidden Request"}, 403

    # build ratings
    ratings = pd.DataFrame(columns=['userId', 'movieId', 'rating'])
    ratings['movieId'] = pd.Series(ids)
    ratings['userId'] = 21
    ratings['rating'] = 5

    # get recommendation
    recommendation_titles, recommendation_movieIds = RS.get_recommendations_for_user(
        ratings_user=ratings, n_rec=20)

    # prepare paths, title and description
    response = df[df.movieId.isin(recommendation_movieIds)]

    # turn dummy encoding back
    genres = []

    for i in range(response.shape[0]):
        df_tmp = response.iloc[i, :].T[['Adventure', 'Animation', 'Children', 'Comedy', 'Fantasy', 'Romance', 'Drama', 'Action',
                                        'Crime', 'Thriller', 'Horror', 'Mystery', 'Sci-Fi', 'IMAX', 'Documentary', 'War', 'Musical', 'Western', 'Film-Noir']]
        genres.append(list(df_tmp[df_tmp == 1].index))

    response['genres'] = pd.Series(genres)

    # filter columns
    response = response[['title', 'description', 'poster_path',
                         'vote_average', 'actor1', 'actor2', 'actor3', 'year', 'genres']]
    response = response.to_json(orient="index")
    return response

@app.route("/getRandomMovies", methods=['GET'])
@cross_origin()
def getRandomMovies():
    # check balance
    if not check_balance():
        return {"message": "Cost limit reached"}, 403

    print(request.args)
    # authentication
    if request.args.get("key") != MIDDLEWARE_KEY:
        return {"message": "Forbidden Request"}, 403

    # draw samples
    random_recommendations = draw_samples(
        top_movies=top_movies, top_genres=top_genres)

    # turn dummy encoding back
    genres = []

    for i in range(random_recommendations.shape[0]):
        df_tmp = random_recommendations.iloc[i, :].T[['Adventure', 'Animation', 'Children', 'Comedy', 'Fantasy', 'Romance', 'Drama',
                                                      'Action', 'Crime', 'Thriller', 'Horror', 'Mystery', 'Sci-Fi', 'IMAX', 'Documentary', 'War', 'Musical', 'Western', 'Film-Noir']]
        genres.append(list(df_tmp[df_tmp == 1].index))

    # add genres as column to df
    random_recommendations = random_recommendations.copy()
    random_recommendations.loc[:, 'genres'] = pd.Series(genres)

    # filter columns
    random_recommendations = random_recommendations[[
        'title', 'description', 'poster_path', 'vote_average', 'actor1', 'actor2', 'actor3', 'year', 'genres', 'movieId']]
    response = random_recommendations.to_json(orient="index")

    return response

@app.route("/speech2text", methods=['POST'])
@cross_origin()
def speech2text():
    if not check_balance():
        return {"message": "Cost limit reached"}, 403

    if request.method != 'POST' or request.form['key'] != MIDDLEWARE_KEY:
        return {"message": "Forbidden request"}, 403

    if 'audiofile' not in request.files.keys():
        return {"message": "Audiofile missing!"}, 400

    if not request.files['audiofile'].filename.endswith('.wav'):
        return {"message": "Audiofile must be in .wav format!"}, 400

    # tmp store file
    save_path = str(datetime.now()) + "temp.wav"
    request.files['audiofile'].save(save_path)

    # continue processing...
    audio_config = speechsdk.audio.AudioConfig(filename=save_path)
    speech_recognizer = speechsdk.SpeechRecognizer(
        speech_config=speech_config, audio_config=audio_config)

    # get text
    result = speech_recognizer.recognize_once_async().get()
    text = result.text

    print(text)

    # delete tmp file
    os.remove(save_path)

    # make request to azure albert
    r = requests.post(getEmbeddingUrl, json={'text': text})

    print(r)
    data = r.json()

    # transform response
    embedding_flattened, shape = data['embedding'], data['shape']
    embedding = torch.tensor(embedding_flattened).reshape(shape)

    # calculate cosine similarity between descriptions
    distances = pairwise_distances(
        descriptions, embedding.reshape(1, 768), metric='cosine')
    indices = np.argsort(distances, axis=0).tolist()
    indices = indices[:10]

    # get recommendations
    recommendations = pd.DataFrame(columns=df.columns)
    for index in indices:
        recommendations = pd.concat((recommendations, df.iloc[index, :]))

    # turn dummy encoding back
    genres = []

    for i in range(recommendations.shape[0]):
        df_tmp = recommendations.iloc[i, :].T[['Adventure', 'Animation', 'Children', 'Comedy', 'Fantasy', 'Romance', 'Drama', 'Action',
                                               'Crime', 'Thriller', 'Horror', 'Mystery', 'Sci-Fi', 'IMAX', 'Documentary', 'War', 'Musical', 'Western', 'Film-Noir']]
        genres.append(list(df_tmp[df_tmp == 1].index))

    genres = pd.Series(genres)
    genres.index = recommendations.index
    recommendations['genres'] = genres

    # filter columns
    recommendations = recommendations[['title', 'description', 'poster_path',
                                       'vote_average', 'actor1', 'actor2', 'actor3', 'year', 'genres']]
    recommendations = recommendations.to_json(orient="index")
    return recommendations

@app.route("/testSpeech2Text", methods=['GET'])
@cross_origin()
def testSpeech2Text():
    if not check_balance():
        return {"message": "Cost limit reached"}, 403

    # send audio to speech2text
    audio_config = speechsdk.audio.AudioConfig(filename="test.wav")
    speech_recognizer = speechsdk.SpeechRecognizer(
        speech_config=speech_config, audio_config=audio_config)

    # get text
    result = speech_recognizer.recognize_once_async().get()
    text = result.text
    return text

# start application
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5501)