from bs4 import BeautifulSoup
import pytest

from app.routers.event import create_event
from app.routers.weekview import get_week_dates

def create_weekview_event(events, session, user):
    for event in events:
        create_event(
            db=session,
            title='test',
            start=event.start,
            end=event.end,
            owner_id=user.id,
            color=event.color
        )

def test_get_week_dates(weekdays, sunday):
    week_dates = list(get_week_dates(sunday))
    for i in range(6):
        assert week_dates[i].strftime('%A') == weekdays[i]

def test_weekview_day_names(session, user, client, weekdays):
    response = client.get("/week/2021-1-3")
    soup = BeautifulSoup(response.content, 'html.parser')
    day_divs = soup.find_all("div", {"class": 'day-name'})
    for i in range(6):
        assert weekdays[i][:3].upper() in str(day_divs[i])

def test_weekview_day_dates(session, user, client, sunday):
    response = client.get("/week/2021-1-3")
    soup = BeautifulSoup(response.content, 'html.parser')
    day_divs = soup.find_all("span", {"class": 'date-nums'})
    week_dates = list(get_week_dates(sunday))
    for i in range(6):
        time_str = f'{week_dates[i].day} / {week_dates[i].month}'
        assert time_str in day_divs[i]

@pytest.mark.parametrize(
    "date,event",
    [("2021-1-31", 'event1'),
     ("2021-1-31", 'event2'),
     ("2021-2-3", 'event3')]
)
def test_weekview_html_events(
    event1, event2, event3, session, user, client, date, event
):
    create_weekview_event([event1, event2, event3], session=session, user=user)
    response = client.get(f"/week/{date}")
    soup = BeautifulSoup(response.content, 'html.parser')
    assert event in str(soup.find("div", {"id": event}))


import json
from os import remove
from pathlib import Path

from polystar.models.box import Box
from polystar.models.image import load_image
from polystar.utils.path import copy_file, move_file
from research.common.datasets.roco.roco_annotation import ROCOAnnotation
from research.common.datasets.roco.roco_dataset_builder import ROCODatasetBuilder
from research.common.datasets.roco.zoo.roco_dataset_zoo import ROCODatasetsZoo
from research.constants import TWITCH_DSET_DIR, TWITCH_ROBOTS_VIEWS_DIR
from research.dataset.twitch.mask_detector import has_bonus_icon, is_aerial_view

AERIAL_DIR = TWITCH_DSET_DIR / "v2" / "aerial-views"
RUNES_DIR = TWITCH_DSET_DIR / "v2" / "runes"

def match_on_dataset(builder: ROCODatasetBuilder):
    twitch_id = builder.main_dir.name
    hd_images_directory = TWITCH_ROBOTS_VIEWS_DIR / twitch_id

    dataset_v2_directory = TWITCH_DSET_DIR / "v2" / twitch_id

    _move_images_with_720p_annotations(builder, dataset_v2_directory, hd_images_directory, twitch_id)
    _copy_changes_locks(builder, dataset_v2_directory)
    _move_aerials_views(hd_images_directory)

def _move_images_with_720p_annotations(
    builder: ROCODatasetBuilder, dataset_v2_directory: Path, hd_images_directory: Path, twitch_id: str
):
    dataset = builder.build_lazy()
    missing_images = []
    for image_file, annotation, _ in dataset:
        hd_image_file = hd_images_directory / image_file.name
        if hd_image_file.exists():
            hd_image = load_image(hd_image_file)
            if has_bonus_icon(hd_image):
                remove(hd_image_file)
                continue
            elif is_aerial_view(hd_image):
                directory = AERIAL_DIR
            elif annotation.has_rune:
                directory = RUNES_DIR
            else:
                directory = dataset_v2_directory
            move_file(hd_image_file, directory / "image")
            _scale_annotation(annotation, height=1080, width=1920)
            annotation.save_in_directory(directory / "image_annotation", image_file.stem)
        else:
            missing_images.append(str(image_file))
    print(f"{len(missing_images)} missing images in {twitch_id}")
    (hd_images_directory / "missing.json").write_text(json.dumps(missing_images))

def _scale_annotation(annotation: ROCOAnnotation, height: int, width: int):
    vertical_ratio, horizontal_ratio = height / annotation.h, width / annotation.w

    for obj in annotation.objects:
        obj.box = Box.from_positions(
            x1=int(obj.box.x1 * horizontal_ratio),
            y1=int(obj.box.y1 * vertical_ratio),
            x2=int(obj.box.x2 * horizontal_ratio),
            y2=int(obj.box.y2 * vertical_ratio),
        )

    annotation.w, annotation.h = width, height

def _copy_changes_locks(builder, dataset_v2_directory):
    for task in ("colors", "digits"):
        changes_lock = builder.main_dir / f"{task}/.changes"
        if changes_lock.exists():
            copy_file(changes_lock, dataset_v2_directory / task)

def _move_aerials_views(hd_images_directory):
    for hd_image_file in hd_images_directory.glob("*.jpg"):
        image = load_image(hd_image_file)
        if has_bonus_icon(image):
            remove(hd_image_file)
        elif is_aerial_view(image):
            move_file(hd_image_file, AERIAL_DIR / "unannotated_image")

if __name__ == "__main__":
    for _builder in ROCODatasetsZoo.TWITCH:
        match_on_dataset(_builder)
    for _new_twitch_id in ("470149066", "470152932"):
        _move_aerials_views(TWITCH_ROBOTS_VIEWS_DIR / _new_twitch_id)


import requests
import json

get_headers = {
    'Authorization': 'Bearer keydq2SURfHCN4Aig'
    }

url = 'https://api.airtable.com/v0/appJeyihmd9jyLKy1/TableNew?maxRecords=100&view=Orders'
response = requests.get(url, headers=get_headers)
# print(donors_response)
data = response.json()
# print(data.keys())
# print((type(data)))

dumps = json.dumps(data)
# print(type(dumps))
airtable_order_id = []
airtable_order_status = []
orders_dict = {}

for j in data['records']:
    airtable_order_id.append(j['fields']['OrderId'])
    airtable_order_status.append(j['fields']['OrderStatus'])

# print(airtable_order_id)
# print(airtable_order_status)

# dict(list(enumerate(values)))

airtable_dict = dict(zip(airtable_order_id,airtable_order_status))
print("airtable_dict : ", airtable_dict)

# for j in data['records']:
#     Orders_list.append(j['fields']['OrderId'])

# print(Orders_list)

# sample = f'''{{'order_id': 400}}'''

# update_url = 'https://api.airtable.com/v0/appJeyihmd9jyLKy1/TableNew?maxRecords=20&view=Orders'
# update_headers = {
#     'Authorization': 'Bearer keydq2SURfHCN4Aig',
#     'Content-Type': 'application/json'
# }
# update_data = {
#     "fields": {
#     # "Name": 33,
#     "OrderValue": sample,
#     "OrderId": 40,
#     },
# #   "createdTime": "2021-01-02T20:33:49.000Z"
# }

# # print(update_data)
# update_response = requests.post(update_url, headers=update_headers, json=update_data)

# url= 'http://192.168.43.8/cam-lo.jpg'

# while True:
#     image = urllib.request.urlopen(url)
#     imgnp = np.array(bytearray(image.read()),dtype=np.uint8)
#     img = cv2.imdecode(imgnp, -1)
#     for qrcode in decode(img):
#         myData = qrcode.data.decode('utf-8')
#         print("mydata : ", myData)
#         file1 = open('sample.txt','a')
#         file1.write(myData)
#         file1.write("\n")
#         update_url = 'https://api.airtable.com/v0/appJeyihmd9jyLKy1/Table?maxRecords=20&view=Orders'
#         update_headers = {
#             'Authorization': 'Bearer keydq2SURfHCN4Aig',
#             'Content-Type': 'application/json'
#         }
#         update_data = {
#             "fields": {
#             # "Name": 33,
#             "OrderId": myData
#         },
#         #   "createdTime": "2021-01-02T20:33:49.000Z"
#         }

#         # print(update_data)
#         update_response = requests.post(update_url, headers=update_headers, json=update_data)
#         pts = np.array([qrcode.polygon],np.int32)
#         pts = pts.reshape((-1,1,2))
#         cv2.polylines(img,[pts],True,(51,255,255),5)
#         pts2 = qrcode.rect
#         cv2.putText(img,myData,(pts2[0],pts2[1]), cv2.FONT_HERSHEY_SIMPLEX,0.9,(51,255,255),2)
#     cv2.imshow('Result : ', img)
#     cv2.waitKey(1)
# cap.release()


"""Клавиатуры"""

from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, \
    KeyboardButton, ReplyKeyboardMarkup

# стартовое меню
start_menu = ReplyKeyboardMarkup(keyboard=[
    [KeyboardButton(text='Блокнот'), KeyboardButton(text='Погода')],
    [KeyboardButton(text='Напоминалка'), KeyboardButton(text='AI')]],
    resize_keyboard=True,
    input_field_placeholder='Выберите команду из меню')

# меню напоминалки
reminder_menu = ReplyKeyboardMarkup(keyboard=[
    [KeyboardButton(text='Добавить')],
    [KeyboardButton(text='Назад')]],
    resize_keyboard=True,
    input_field_placeholder='Выберите команду из меню')

# кнопка для отправки геолоки
send_location = ReplyKeyboardMarkup(keyboard=[
    [KeyboardButton('Отправить геолокацию',
                    request_location=True),
     KeyboardButton(text='Назад')]],
    resize_keyboard=True,
    input_field_placeholder='Отправьте вашу геолокацию:')

add_record_button = InlineKeyboardButton('добавить',
                                         callback_data='add_record')

add_record = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton('добавить', callback_data='add_record')]])

change_reminder_time = InlineKeyboardMarkup(inline_keyboard=[
    [InlineKeyboardButton('Отложить на 15 минут',
                          callback_data='fifteen_minutes')],
    [InlineKeyboardButton('Отложить на 1 час',
                          callback_data='one_hour')],
    [InlineKeyboardButton('Подтвердить',
                          callback_data='reminder_done')]])

# универсальная кнопка назад (в стартовое меню)
back = ReplyKeyboardMarkup(keyboard=[[KeyboardButton(text='Назад')]],
                           resize_keyboard=True)


import json
from pathlib import Path

import pytest

from hijridate import Gregorian, Hijri

def load_params_from_json():
    data = []
    files = Path(__file__).parent.joinpath("fixtures").glob("*.json")
    for file in files:
        file_content = json.loads(file.read_text())
        # skip test data when the last element is False
        file_data = [
            [tuple(i) for i in x[0]] for x in file_content if x[-1] is not False
        ]
        data += file_data
    params = [tuple(x) for x in data]
    params_reversed = [tuple(x[::-1]) for x in data]
    return params, params_reversed

hijri_gregorian_params, gregorian_hijri_params = load_params_from_json()

@pytest.mark.parametrize("test_input, expected", hijri_gregorian_params)
def test_convert_hijri_to_gregorian(test_input, expected):
    year, month, day = test_input
    hijri = Hijri(year, month, day, validate=False)
    converted = hijri.to_gregorian().datetuple()
    assert converted == expected

@pytest.mark.parametrize("test_input, expected", gregorian_hijri_params)
def test_convert_gregorian_to_hijri(test_input, expected):
    year, month, day = test_input
    gregorian = Gregorian(year, month, day)
    converted = gregorian.to_hijri().datetuple()
    assert converted == expected


from pathlib import Path
from typing import Union

import numpy as np
import pandas as pd
import pytorch_lightning as pl
import torch
from pytorch_lightning.loggers.wandb import WandbLogger
from sklearn.preprocessing import MinMaxScaler
from torch import nn
from torch.nn import functional as F
from torch.utils.data import DataLoader, Dataset, random_split

from utils import current2formfactor

class LengthReconstructionDataset(Dataset):
    """
    Dataset for reconstructing the bunch length from RF settings and the THz spectrum.
    """

    def __init__(
        self,
        path: Union[Path, str],
        normalize_rf: bool = True,
        normalize_formfactors: bool = True,
        normalize_lengths: bool = True,
    ) -> None:
        self.normalize_rf = normalize_rf
        self.normalize_formfactors = normalize_formfactors
        self.normalize_lengths = normalize_lengths

        self.rf_settings, self.formfactors, self.bunch_lengths = self.load_data(path)

        if self.normalize_rf:
            self.rf_scaler = MinMaxScaler().fit(self.rf_settings)
            self.rf_settings = self.rf_scaler.transform(self.rf_settings)
        if self.normalize_formfactors:
            self.formfactor_scaler = MinMaxScaler().fit(self.formfactors)
            self.formfactors = self.formfactor_scaler.transform(self.formfactors)
        if self.normalize_lengths:
            self.length_scaler = MinMaxScaler().fit(self.bunch_lengths)
            self.bunch_lengths = self.length_scaler.transform(self.bunch_lengths)

        self.rf_settings = torch.tensor(self.rf_settings, dtype=torch.float32)
        self.formfactors = torch.tensor(self.formfactors, dtype=torch.float32)
        self.bunch_lengths = torch.tensor(self.bunch_lengths, dtype=torch.float32)

    def load_data(
        self, path: Union[Path, str]
    ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
        """
        Load data saved at `path` from disk and return the parts of it relevant to this
        dataset.
        """
        path = Path(path) if isinstance(path, str) else path

        df = pd.read_pickle(path)
        rf_settings = df[["chirp", "chirpL1", "chirpL2", "curv", "skew"]].values

        ss = np.stack(
            [np.linspace(0, 300 * df.loc[i, "slice_width"], num=300) for i in df.index]
        )
        bunch_lengths_m = np.expand_dims(ss.max(axis=1) - ss.min(), axis=1)

        currents = np.stack(df["slice_I"].values)

        formfactors = np.array(
            [
                current2formfactor(
                    ss, currents, grating="both", n_shots=1, clean=False
                )[1]
                for ss, currents in zip(ss, currents)
            ]
        )

        return rf_settings, formfactors, bunch_lengths_m

    def __len__(self) -> int:
        return len(self.bunch_lengths)

    def __getitem__(
        self, index: int
    ) -> tuple[tuple[torch.Tensor, torch.Tensor], torch.Tensor]:
        rf_setting = self.rf_settings[index]
        formfactor = self.formfactors[index]
        bunch_length = self.bunch_lengths[index]

        return (rf_setting, formfactor), bunch_length

class LengthReconstructor(pl.LightningModule):
    """Neural networks for reconstructing currents at EuXFEL."""

    def __init__(self) -> None:
        super().__init__()

        self.mlp = nn.Sequential(
            nn.Linear(5 + 240, 200),
            nn.ReLU(),
            nn.Linear(200, 100),
            nn.ReLU(),
            nn.Linear(100, 50),
            nn.ReLU(),
            nn.Linear(50, 1),
            nn.ReLU(),
        )

    def forward(
        self, rf_settings: torch.Tensor, formfactors: torch.Tensor
    ) -> torch.Tensor:
        concatenated = torch.cat([rf_settings, formfactors], dim=1)
        bunch_length = self.mlp(concatenated)
        return bunch_length

    def configure_optimizers(self) -> torch.optim.Optimizer:
        return torch.optim.Adam(self.parameters(), lr=1e-3)

    def training_step(self, train_batch: torch.Tensor, batch_idx: int) -> torch.Tensor:
        (rf_settings, formfactors), bunch_lengths = train_batch
        predictions = self.forward(rf_settings, formfactors)
        loss = F.mse_loss(predictions, bunch_lengths)
        self.log("train/loss", loss)
        mae = F.l1_loss(predictions, bunch_lengths)
        self.log("train/mae", mae)
        return loss

    def validation_step(self, val_batch: torch.Tensor, batch_idx: int) -> torch.Tensor:
        (rf_settings, formfactors), bunch_lengths = val_batch
        predictions = self.forward(rf_settings, formfactors)
        loss = F.mse_loss(predictions, bunch_lengths)
        self.log("val/loss", loss)
        mae = F.l1_loss(predictions, bunch_lengths)
        self.log("val/mae", mae)
        return loss

def main() -> None:
    dataset = LengthReconstructionDataset("data/zihan/data_20220905.pkl")
    train_dataset, val_dataset = random_split(dataset, [0.8, 0.2])

    train_loader = DataLoader(
        train_dataset, batch_size=64, shuffle=True, num_workers=40
    )
    val_loader = DataLoader(val_dataset, batch_size=64, num_workers=40)

    model = LengthReconstructor()

    wandb_logger = WandbLogger(project="ml-lps-recon-length")

    trainer = pl.Trainer(max_epochs=1000, accelerator="auto", logger=wandb_logger)
    trainer.fit(model, train_loader, val_loader)

if __name__ == "__main__":
    main()


import os
import webbrowser
import pyperclip
import multiprocessing
import PySimpleGUI as sg
import traceback
import sys
from os.path import exists
from threading import Thread
from threading import Lock
from random import randint, randrange
from pydub import AudioSegment
from multiprocessing.pool import ThreadPool
from PyUtils.SkyAudioEncoder import SkyAudioEncoder
from PyUtils.MusicUtils import MusicUtils
from PyUtils.FileUtils import FileUtils
from PyUtils.FileUtils import Exts
from PyUtils.Functions import *
from PyUtils.Logger import Logger
from Gui.AudioData import AudioData
from Gui.ReportBatchCmd import ReportBatchCmd
from Gui.ReportAudioDetails import ReportAudioDetails
from Settings.AppInfo import AppInfo
from Settings.ProfileManager import ProfileManager

class AudioLogicLayer:

    STR_INFO_POPUP = "Info"
    STR_ERROR_POPUP = "Error"
    STR_CANCEL = "Cancel"
    STR_OK = "Ok"
    batchReportMutex = Lock()

    def __init__(self, app_dir):
        self.app = AppInfo(app_dir)
        self.profile_manager = ProfileManager(app_dir)
        self.encoder = SkyAudioEncoder(self.app.audio_encoder_dir)
        self.player = MusicUtils()
        self.console_output = "CK Audio Manager initialized...\n"
        self._console_has_change = True

    def generate_list_audio_data(self):
        """
        Generates the list of audio data used to fill the AudioWindow table.
        :return:
        """
        _log = Logger.get()
        _log.debug("-- generate_list_audio_data()")
        skyrim_files_path = self.profile_manager.profile_db_dir()
        comments_csv = self.app.settings_obj.comments_file
        actors_csv = self.app.settings_obj.actors_file
        scene_order_csv = self.app.settings_obj.scene_order_file
        list_audio_data = AudioData.generate_list_audio_data(skyrim_files_path, comments_csv, actors_csv, scene_order_csv)
        return list_audio_data

    def create_audio_details_report(self, list_audio_data, ask_popup=True):
        _log = Logger.get()
        _log.debug(" -- create_audio_details_report()")
        list_details = []
        item: AudioData
        for item in list_audio_data:
            status_dict = AudioLogicLayer.file_status_dic(item.file_path)
            details = ReportAudioDetails()
            # audio status
            details.mp3 = status_dict["mp3"]
            details.wav = status_dict["wav"]
            details.xwm = status_dict["xwm"]
            details.lip = status_dict["lip"]
            details.fuz = status_dict["fuz"]
            # dialog data
            details.quest_id = item.quest_id
            details.actor = item.actor_name
            details.file = item.file_name
            details.subtitle = item.subtitle
            details.file_path = item.file_path
            details.dialog_type = item.dialog_type
            details.emotion = item.emotion
            details.voice_type = item.voice_type
            details.topic_id = item.topic_id
            details.branch_id = item.branch_id
            details.scene_id = item.scene_id
            details.scene_phase = item.scene_phase
            list_details.append(details)
        # file_name = ReportAudioDetails.export_report(list_details, self.app)
        file_name = ReportAudioDetails.export_report(list_audio_details=list_details,
                                                     output_dir=self.profile_manager.profile_docgen_dir(),
                                                     app_name=self.app.app_name_LARGE,
                                                     url_github=self.app.url_github)
        _log.debug("file_name:" + str(file_name))
        _log.debug("Report generation finished.")
        self._console_add("Report generation finished.")
        if ask_popup:
            popup_ret = ""
            popup_text = "Report generation finished. Do you want to open it?"
            popup_ret = sg.popup_ok_cancel(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_ERROR_POPUP)
            self._console_add(popup_text)
            url_report = "file:///" + os.path.realpath(file_name)
            if popup_ret != AudioLogicLayer.STR_CANCEL:
                webbrowser.open(url_report, new=2)

        return file_name

    @staticmethod
    def file_status_dic(sound_path: str):
        """
        Return a dict with the file status.
        :param sound_path:
        :return:
        """
        if sound_path == "":
            return ""
        file_status_dict = {
            "mp3": "missing",
            "wav": "missing",
            "xwm": "missing",
            "lip": "missing",
            "fuz": "missing"
        }
        mp3_file = FileUtils.change_ext(sound_path, Exts.EXT_MP3)
        wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
        xwm_file = FileUtils.change_ext(sound_path, Exts.EXT_XWM)
        lip_file = FileUtils.change_ext(sound_path, Exts.EXT_LIP)
        fuz_file = FileUtils.change_ext(sound_path, Exts.EXT_FUZ)
        msg = ""
        # mp3
        if exists(mp3_file):
            file_status_dict["mp3"] = "ok"
        # wav
        if exists(wav_file):
            file_status_dict["wav"] = "ok"
        # xwm
        if exists(xwm_file):
            file_status_dict["xwm"] = "ok"
        # lip
        if exists(lip_file):
            file_status_dict["lip"] = "ok"
        # fuz
        if exists(fuz_file):
            file_status_dict["fuz"] = "ok"
        return file_status_dict

    @staticmethod
    def file_status(sound_path: str):
        """
        Return a string with the file status.
        :return:
        """
        # print("** sound_path:" + sound_path)
        if sound_path == "":
            return ""
        mp3_file = FileUtils.change_ext(sound_path, Exts.EXT_MP3)
        wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
        xwm_file = FileUtils.change_ext(sound_path, Exts.EXT_XWM)
        lip_file = FileUtils.change_ext(sound_path, Exts.EXT_LIP)
        fuz_file = FileUtils.change_ext(sound_path, Exts.EXT_FUZ)
        msg = ""
        # mp3
        if exists(mp3_file):
            msg = " mp3[ok] "
        # wav
        if exists(wav_file):
            msg += " wav[ok] "
        else:
            msg += "wav[missing] "
        # xwm
        if exists(xwm_file):
            msg += "xmw[ok] "
        else:
            msg += "xmw[missing] "
        # lip
        if exists(lip_file):
            msg += "lip[ok] "
        else:
            msg += "lip[missing] "
        # fuz
        if exists(fuz_file):
            msg += "fuz[ok] "
        else:
            msg += "fuz[missing] "
        return msg

    def set_sound(self, sound_path: str):
        """
        Screen Element: Select row
        :param sound_path:
        :return:
        """
        _log = Logger.get()
        self._console_add("set_sound() sound_path: " + sound_path)
        _log.debug("-- set_sound() sound_path:" + sound_path)
        if sound_path.strip() == "":
            return
        ret_val = self.encoder.try_to_gen_wav(sound_path, force_generation=False)
        if ret_val == SkyAudioEncoder.RET_SUCCESS:
            wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
            self.player.set(wav_file)
        else:
            ret_msg = self.encoder.get_last_error()
            err_description = self.encoder.get_last_stdout()
            popup_text = "Error selecting track " + sound_path + ": " + ret_msg + "\n\n" + err_description
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title="Play Audio Error")
            self._console_add(popup_text)

    def play_sound(self, sound_path: str):
        """
        Screen Element: Play Button
        :param sound_path:
        :return:
        """
        _log = Logger.get()
        self._console_add("play_sound() sound_path: " + sound_path)
        _log.debug("-- play_sound() sound_path:" + sound_path)
        if sound_path == "":
            popup_text = "No sound file selected."
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_INFO_POPUP)
            self._console_add(popup_text)
            return
        ret_val = self.encoder.try_to_gen_wav(sound_path, force_generation=False)
        if ret_val == SkyAudioEncoder.RET_SUCCESS:
            wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
            self.player.play(wav_file)
        else:
            ret_msg = self.encoder.get_last_error()
            err_description = self.encoder.get_last_stdout()
            popup_text = "Error playing track " + sound_path + ": " + ret_msg + "\n\n" + err_description
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title="Play Audio Error")
            self._console_add(popup_text)

    def stop_sound(self):
        """
        Screen Element: Stop Button
        :return:
        """
        _log = Logger.get()
        self._console_add("stop_sound()")
        _log.debug("-- stop_sound()")
        self.player.stop()

    def pause_sound(self):
        """
        Screen Element: Pause Button
        :return:
        """
        _log = Logger.get()
        self._console_add("pause_sound()")
        _log.debug("-- stop_sound()")
        self.player.pause()

    def set_volume(self, volume: int):
        """
        Screen Element: Volume bar.
        :param volume:
        :return:
        """
        _log = Logger.get()
        self._console_add("set_volume() volume:" + str(volume))
        _log.debug("-- set_volume() " + str(volume))
        v_in = volume/100
        self.player.set_volume(v_in)

    def open_folder(self, sound_path: str):
        """
        Screen Element: Open Folder Button
        :param sound_path:
        :return:
        """
        _log = Logger.get()
        _log.debug("-- open_folder()")
        self._console_add("open_folder() sound_path:" + sound_path)
        mp3_file = FileUtils.change_ext(sound_path, Exts.EXT_MP3)
        lip_file = FileUtils.change_ext(sound_path, Exts.EXT_LIP)
        wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
        xwm_file = FileUtils.change_ext(sound_path, Exts.EXT_XWM)
        fuz_file = FileUtils.change_ext(sound_path, Exts.EXT_FUZ)
        if exists(wav_file):
            FileUtils.open_file_on_file_explorer(wav_file)
            return
        if exists(xwm_file):
            FileUtils.open_file_on_file_explorer(xwm_file)
            return
        if exists(fuz_file):
            FileUtils.open_file_on_file_explorer(fuz_file)
            return
        if exists(lip_file):
            FileUtils.open_file_on_file_explorer(lip_file)
            return
        if exists(mp3_file):
            FileUtils.open_file_on_file_explorer(mp3_file)
            return
        popup_text = "File " + sound_path + " not found."
        sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_ERROR_POPUP)
        self._console_add(popup_text)

    def copy_track_name(self, sound_path: str):
        """
        Copy the track name to the clipboard.
        :param sound_path:
        :return:
        """
        _log = Logger.get()
        self._console_add("copy_track_name() sound_path:" + sound_path)
        _log.debug("-- copy_track_name()")
        pyperclip.copy(sound_path)
        popup_text = "Track name " + sound_path + " was copied to the clipboard."
        sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_INFO_POPUP)
        self._console_add(popup_text)

    def copy_track_info(self, sound_path: str, list_audio_data):
        """
        Copy track info to clipboard.
        :param sound_path:
        :param list_audio_data:
        """
        _log = Logger.get()
        _log.debug("-- copy_track_info()")
        self._console_add("copy_track_info() sound_path:" + sound_path)
        data: AudioData
        out_data = ""
        for data in list_audio_data:
            if data.file_path == sound_path:
                out_data = data.to_string()
        pyperclip.copy(out_data)
        popup_text = "Track information as copied to the clipboard: \n" + out_data
        sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_INFO_POPUP)
        self._console_add(popup_text)

    def audio_gen_lip(self, sound_path: str, list_audio_data):
        "NOT WORKING PROPERLY"
        _log = Logger.get()
        _log.debug("-- audio_gen_lip()")
        self._console_add("audio_gen_lip() sound_path:" + sound_path)
        data: AudioData
        subtitles = ""
        wavfile = sound_path + ".wav"
        if exists(wavfile) != True:
            msg = "Error, could not find WAV file " + wavfile + " required to generate the lip file!"
            _log.warning("**WARNING** " + msg)
            self._console_add(msg)
            sg.popup_ok_cancel(msg, keep_on_top=True, icon=self.app.app_icon_ico,
                               title=AudioLogicLayer.STR_ERROR_POPUP)
            return False
        for data in list_audio_data:
            if data.file_path == sound_path:
                subtitles = data.subtitle
        # 0 - Creation Kit Exe
        # 1 - WAV file
        # 3 - Subtitle
        cmd = "{0}  -GenerateSingleLip:\"{1}\" \"{2}\"".format(self.app.creation_kit_exe, sound_path, subtitles)
        # print("##############" + cmd)

    def audio_gen_xwm(self, sound_path: str):
        """
        Generate XWM file.
        :param sound_path:
        """
        _log = Logger.get()
        _log.debug("-- audio_gen_xwm()")
        self._console_add("audio_gen_xwm(): " + sound_path)
        if sound_path.strip() == "":
            popup_text = "Error: No sound file selected!"
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico,
                     title=AudioLogicLayer.STR_INFO_POPUP)
            self._console_add(popup_text)
            return
        xwm_file = FileUtils.change_ext(sound_path, Exts.EXT_XWM)
        wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
        # in case a XWM file already exists, ask first
        if exists(xwm_file):
            popup_text = "The following file is going to be overwritten: \n" + str(xwm_file) +\
                         "\n\nDo you want to continue?"
            popup_ret = sg.popup_ok_cancel(popup_text, keep_on_top=True, icon=self.app.app_icon_ico,
                                 title=AudioLogicLayer.STR_INFO_POPUP)
            self._console_add(popup_text)
            if popup_ret == AudioLogicLayer.STR_CANCEL:
                return
        # XWM file is going to be generated!
        # in case WAV does not exist, create one
        ret_flag = True
        ret_int = SkyAudioEncoder.RET_SUCCESS
        ret_msg = ""
        ret_stdout = ""
        if not exists(wav_file):
            [ret_flag, ret_val, ret_msg, ret_stdout] = self._generate_wav_if_not_exit(sound_path, xwm_to_wav=False)
        if not ret_flag:
            _log.warn("Last Stdout:" + ret_stdout)
            _log.warn("Last error:" + ret_msg + " for sound_path:" + sound_path)
            self._console_add("Last Stdout:\n" + ret_stdout)
            self._console_add("Last error:\n" + ret_msg + " for sound_path:" + sound_path)
            popup_text = "Error Generating XWM file: Error creating WAV file.\nSound Path: " + sound_path + \
                         "\nError Message:" + ret_msg
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_ERROR_POPUP)
            self._console_add(popup_text)
            return
        ret_val = self.encoder.wav_to_xwm(sound_path)
        ret_stdout = self.encoder.get_last_stdout()
        ret_msg = self.encoder.get_last_error()
        _log.warn("Last Stdout:\n" + ret_stdout)
        self._console_add("Last Stdout:\n" + ret_stdout)
        if ret_val != SkyAudioEncoder.RET_SUCCESS:
            _log.error("Last error:" + ret_msg + " for sound_path:" + sound_path)
            self._console_add("Last error:" + ret_msg + " for sound_path:" + sound_path)
            popup_text = "Error encoding WAV into XWM.\nSound Path:" + sound_path + "\nError Code:" + str(ret_val) + \
                         ".\nError Message:" + ret_msg
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico,
                     title=AudioLogicLayer.STR_ERROR_POPUP)
            self._console_add(popup_text)
            return
        popup_text = "Success generating XWM file."
        sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_INFO_POPUP)
        self._console_add(popup_text)

    def audio_gen_fuz(self, sound_path: str):
        """
        Generate fuz file.
        :param sound_path:
        :return:
        """
        _log = Logger.get()
        _log.debug("-- audio_gen_fuz()")
        self._console_add("audio_gen_fuz() sound_path:" + sound_path)
        fuz_file = FileUtils.change_ext(sound_path, Exts.EXT_FUZ)
        ret_val = self.encoder.fuz(sound_path)
        if ret_val != SkyAudioEncoder.RET_SUCCESS:
            popup_text = "Error encoding file into FUZ format: " + self.encoder.get_last_error()
            _log.error("PopUp Error: " + popup_text)
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_ERROR_POPUP)
            self._console_add(popup_text)
            return
        _log.debug("FUZ file " + fuz_file + " generated successfully!")
        popup_text = "Success generating FUZ file " + fuz_file + "."
        sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_INFO_POPUP)
        self._console_add(popup_text)

    def audio_gen_fuz_all(self, list_sound_path, parallel_method: int, ask_popup=True):
        """
        Execute fuz command massively, and returns a report.
        Tests with parallel methods, using the same dataset:
        *Method 1*
        (1) QuickTimer: 32.77095009999999 seconds
        (2) QuickTimer: 19.1045664 seconds (after restart, fow apps)
        (3) QuickTimer: 21.5257244 seconds (no restart, few apps)
        *Method 2*
        (1) QuickTimer: 29.6214035 seconds (many apps opened)
        (2) QuickTimer: 25.2366236 seconds (after restart, fow apps)
        (3) QuickTimer: 15.642155099999997 seconds (no restart, few apps)
        :param list_sound_path:
        :param parallel_method: 1 for one thread per core, 2 for thread ThreadPool (built-in).
        :return:
        """
        _log = Logger.get()
        _log.debug("audio_gen_fuz_all()")
        self._console_add("audio_gen_fuz_all()")
        # popup of confirmation
        if ask_popup:
            popup_text = "This procedure will overwrite any .fuz and xwm pre-existing file.\n If the audios are recoded in mp3 format, wav files are going to be overwritten as well.\n\n Do you want to continue?"
            self._console_add(popup_text)
            _log.debug("popup_text:" + popup_text)
            popup_ret = sg.popup_ok_cancel(popup_text, keep_on_top=True, icon=self.app.app_icon_ico,
                                           title=AudioLogicLayer.STR_INFO_POPUP)
            self._console_add("User option: " + popup_ret)
            if popup_ret == AudioLogicLayer.STR_CANCEL:
                return
        # init the batch generation
        self._console_add("audio_gen_fuz_all() list_sound_path:" + str(list_sound_path) + ", parallel_method:" +
                          str(parallel_method))
        curr_exec_path = self.encoder.get_exe_dir()
        report_list_arg = []
        report_list_async = []
        ncores = multiprocessing.cpu_count()
        if ncores <= 1:
            ncores = 2
        self._console_add("-- ncores:" + str(ncores))
        # print("-- ncores:" + str(ncores))
        splitted_list = split_list(list_sound_path, ncores)
        # Thread managing, one per core
        if parallel_method != 2:
            threads = []
            ret_list = []
            for small_list in splitted_list:
                process = Thread(target=AudioLogicLayer._exec_fuz_list, args=[small_list, curr_exec_path, report_list_arg])
                process.start()
                threads.append(process)
            for process in threads:
                process.join()
        # Builtin thread pool
        else:
            pool = ThreadPool()
            async_result_list = []
            for small_list in splitted_list:
                async_result = pool.apply_async(AudioLogicLayer._exec_fuz_list, (small_list, curr_exec_path, report_list_arg))
                async_result_list.append(async_result)
            for item in async_result_list:
                return_val = item.get()
                report_list_async.append(return_val)
        n_errors = ReportBatchCmd.count_errors(report_list_arg)
        n_success = ReportBatchCmd.count_success(report_list_arg)
        _log.debug("Batch finished. n_errors:" + str(n_errors) + ", n_success:" + str(n_success))
        if ask_popup:
            popup_ret = ""
            popup_text = "Batch execution finished with {0} errors and {1} successes. Do you want to open the report?".format(n_errors, n_success)
            popup_ret = sg.popup_ok_cancel(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_ERROR_POPUP)
            self._console_add(popup_text)
            html_report = ReportBatchCmd.export_report(report_list_arg, self.app.app_dir)
            url_report = "file:///" + os.path.realpath(html_report)
            # print("url_report:" + url_report)
            # print("popup_ret:" + popup_ret)
            if popup_ret != AudioLogicLayer.STR_CANCEL:
                webbrowser.open(url_report, new=2)

    def audio_gen_silent(self, sound_path: str, list_audio_data, ask_popup=True):
        _log = Logger.get()
        _log.debug("-- audio_gen_silent()")
        self._console_add("audio_gen_silent(): " + sound_path)
        # (1) check if no file was selected
        if sound_path.strip() == "":
            popup_text = "Error: No sound file selected!"
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico,
                     title=AudioLogicLayer.STR_INFO_POPUP)
            self._console_add(popup_text)
            return
        # (2) filter the right file text from table data
        audio_text = ""
        for data in list_audio_data:
            if data.file_path == sound_path:
                audio_text = data.subtitle
                break
        # (3) calc usefull info
        sound_no_ext = FileUtils.remove_ext(sound_path)
        reading_time = AudioLogicLayer._calc_reading_time(text_str=audio_text,
                                                          wpm=self.app.settings_obj.audio_wpm,
                                                          word_len=self.app.settings_obj.audio_word_len,
                                                          min_time=self.app.settings_obj.audio_min_time,
                                                          padding=self.app.settings_obj.audio_padding)
        sound_wav = sound_no_ext + ".wav"
        bkp_name = sound_no_ext + ".rand" + str(randint(10000, 99999)) + ".wav.bkp"
        file_already_exist = os.path.exists(sound_wav)
        # (4) Popup and backup
        if ask_popup and file_already_exist:
            popup_text = "Audio file " + sound_wav + " already exists. Continuing will overwrite this file.\n\n" +\
                         "New generated file are going to have " + str(reading_time) + " seconds.\n\n" +\
                         "Are you sure?\n\n"
            popup_ret = sg.popup_ok_cancel(popup_text, keep_on_top=True, icon=self.app.app_icon_ico,
                                           title=AudioLogicLayer.STR_INFO_POPUP)
            if (popup_ret == AudioLogicLayer.STR_CANCEL) or (popup_ret is None):
                _log.debug("-- audio_gen_silent() CANCELED")
                self._console_add("-- audio_gen_silent() CANCELED")
                return
            _log.info("creating backup file " + str(bkp_name))
            self.player.reset()
            os.rename(sound_wav, bkp_name)
        # (5) GENERATE EMPTY AUDIO
        ret_val, trace = AudioLogicLayer._create_silent_audio(sound_no_ext, reading_time)
        # in case of failure, report the error and restore the backup
        if not ret_val:
            _log.error(trace)
            sg.popup_ok(trace, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_INFO_POPUP)
            if file_already_exist:
                os.rename(bkp_name, sound_wav)
            self.player.set(sound_wav)
            return
        # in case of success, delete the backup
        else:
            _log.info("Deleting BACKUP file " + bkp_name)
            os.remove(bkp_name)
            self.player.set(sound_wav)

    def audio_unfuz(self, sound_path: str):
        """
        Decode FUZ file into lip, xwm an wav format.
        :param sound_path: sound file.
        """
        _log = Logger.get()
        _log.debug("-- audio_unfuz()")
        self._console_add("audio_unfuz() sound_path:" + sound_path)
        lip_file = FileUtils.change_ext(sound_path, Exts.EXT_LIP)
        xwm_file = FileUtils.change_ext(sound_path, Exts.EXT_XWM)
        wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
        exit_file = []
        resp = True
        if exists(wav_file):
            exit_file.append(wav_file)
        if exists(xwm_file):
            exit_file.append(xwm_file)
        if exists(lip_file):
            exit_file.append(lip_file)
        if len(exit_file) > 0:
            popup_text = "The following files are going to be overwritten: \n" + str(exit_file) +\
                         "\n\nDo you want to continue?"
            _log.error("PopUp Error: " + popup_text)
            popup_ret = sg.popup_ok_cancel(popup_text, keep_on_top=True, icon=self.app.app_icon_ico,
                                           title=AudioLogicLayer.STR_INFO_POPUP)
            if popup_ret == AudioLogicLayer.STR_CANCEL:
                _log.info("Operation {0} was CANCELLED".format("audio_unfuz()"))
                self._console_add("Operation {0} was CANCELLED".format("audio_unfuz()"))
                return
        ret_val = self.encoder.unfuz(sound_path)
        if ret_val != SkyAudioEncoder.RET_SUCCESS:
            popup_text = "Error decoding file " + sound_path + "\nError Code:" + str(ret_val) + "\n. Error message:" + \
                         self.encoder.get_last_error()
            _log.error("PopUp Error: " + popup_text)
            sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_ERROR_POPUP)
            self._console_add(popup_text)
            return
        _log.debug("UNFUZ on file " + sound_path + " was successful!")
        popup_text = "Success on UNFUZ file " + sound_path + "."
        self._console_add(popup_text)
        sg.Popup(popup_text, keep_on_top=True, icon=self.app.app_icon_ico, title=AudioLogicLayer.STR_INFO_POPUP)

    def get_current_track_len(self):
        """
        Returns track len in seconds.
        :return:
        """
        return self.player.len()

    def get_current_track_progress(self):
        """
        Returns track progress in seconds.
        :return:
        """
        return self.player.position()

    def console_has_change(self):
        """
        Tells if the console has any change or not.
        :return: True or false.
        """
        return self._console_has_change

    def get_console_output(self):
        """
        Retuns the string with the log execution of the last task.
        :return: string with the console output.
        """
        self._console_has_change = False
        return self.console_output

    @staticmethod
    def _exec_fuz_list(list_files, exec_path, list_exec_report):
        """
        Execute the fuz operation for all listed files on list_files. The encoder is instantiated using the exec_path
        as reference. A list of report objects BatchCmdReport is returned and is appended (extend) to the mutable list
        list_exec_report.
        :param list_files: list of sound files where are going to be applied the fuz operation.
        :param exec_path: Exec path to instantidate the SkyAudioEncoder object.
        :param list_exec_report: mutable list of execution reports.
        :return: list of execution reports generated in this thread.
        """
        encoder = SkyAudioEncoder(exec_path)
        thread_report = []
        if len(list_files) == 0:
            return thread_report
        for sound_path in list_files:
            report = ReportBatchCmd()
            fuz_file = FileUtils.change_ext(sound_path, Exts.EXT_FUZ)
            ret_val = encoder.fuz(sound_path)
            report.error_code = ret_val
            if ret_val != SkyAudioEncoder.RET_SUCCESS:
                report.error_flag = False
                report.error_message = encoder.get_last_error()
            else:
                report.error_flag = True
            report.command = encoder.get_last_command()
            report.stdout = encoder.get_last_stdout()
            report.process_code = encoder.get_last_encodder_ret_code()
            report.file_name = sound_path
            report.exe_dir = exec_path
            thread_report.append(report)
        AudioLogicLayer.batchReportMutex.acquire()
        try:
            list_exec_report.extend(thread_report)
        finally:
            AudioLogicLayer.batchReportMutex.release()
        return thread_report

    @staticmethod
    def _calc_reading_time(text_str, wpm=110, word_len=5, min_time=2, padding=0):
        """
        Estimate the reading time in seconds.
        :param text_str: Text to be read.
        :param wpm: words per minute.
        :param word_len: length of each word.
        :param min_time: minimum time.
        :param padding: this value will be added to the generated time.
        :return: extimated reading time.
        """
        # split text in words
        text_list = text_str.split()
        # count words
        total_words = 0
        for current_text in text_list:
            total_words += len(current_text) / word_len
        # calc reading time in seconds
        read_time = (total_words * 60) / wpm
        # add padding
        read_time = read_time + padding
        # ensure min time
        read_time = max([read_time, min_time])
        return round(read_time)

    @staticmethod
    def _create_silent_audio(file_name: str, duration_sec: int):
        try:
            silent_audio = AudioSegment.silent(duration=int(duration_sec) * 1000)  # or be explicit
            silent_audio.export(file_name + ".wav", format="wav")
            return True, "SUCCESS"
        except:
            ex_msg = "Error exporting file " + file_name + ".wav\n'" +\
                     "traceback.format_exc(): " + str(traceback.format_exc()) + "\n" +\
                     "sys.exc_info()[2]: " + str(sys.exc_info()[2])
            return False, ex_msg

    def _generate_wav_if_not_exit(self, sound_path, xwm_to_wav=True):
        """
        Try to generate WAV file if it does not exit.
        :param sound_path: Path of the file to be used on the generation of the wav file.
        :param xwm_to_wav: optional flag to tell the method to try or not to generate a wav file from a xwm.
        :return: return a vector [ret_flag: bool, ret_val: int, ret_msg: str, ret_stdout: str], where ret_flag is True
        if the WAV file already exits, or it was rightly generated. Returns False in case some error occurred
        generating the WAV file. ret_val is the code returned by the encoder. ret_str is the error message returned by
        the encoder, in case of error, and the ret_strout is the console output generated (error os success).
        """
        _log = Logger.get()
        _log.debug("-- _generate_wav_if_not_exit()")
        self._console_add("_generate_wav_if_not_exit()")
        ret_flag = False
        ret_val = SkyAudioEncoder.RET_SUCCESS
        ret_msg = ""
        ret_stdout = ""
        wav_file = FileUtils.change_ext(sound_path, Exts.EXT_WAV)
        xwm_file = FileUtils.change_ext(sound_path, Exts.EXT_XWM)
        mp3_file = FileUtils.change_ext(sound_path, Exts.EXT_MP3)
        _log.debug("wav_file:" + wav_file + ", xwm_file:" + xwm_file + ", mp3_file:" + mp3_file)
        self._console_add("wav_file:" + wav_file + ", xwm_file:" + xwm_file + ", mp3_file:" + mp3_file)
        if not exists(wav_file):
            _log.info("WAV file for <" + sound_path + "> was not found. Search for alternatives: XWM and MP3...")
            self._console_add("WAV file for <" + sound_path + "> was not found. Search for alternatives: XWM and MP3...")
            # try to generate file from XWM
            if exists(xwm_file) and xwm_to_wav:
                _log.info("XWM was found for " + sound_path)
                self._console_add("XWM was found for " + sound_path)
                ret_val = self.encoder.xwm_to_wav(sound_path)
                ret_stdout = self.encoder.get_last_stdout()
                _log.error("Last stdout: " + ret_stdout)
                self._console_add("Last stdout: " + ret_stdout)
                if ret_val != SkyAudioEncoder.RET_SUCCESS:
                    ret_msg = self.encoder.get_last_error()
                    _log.error("Last error: " + ret_msg)
                    self._console_add("Last error: " + ret_msg)
            # try mp3 format
            elif exists(mp3_file):
                _log.info("MP3 was found for " + sound_path)
                self._console_add()
                ret_val = self.encoder.mp3_to_wav(sound_path)
                ret_stdout = self.encoder.get_last_stdout()
                _log.error("Last stdout: " + ret_stdout)
                self._console_add("Last stdout: " + ret_stdout)
                if ret_val != SkyAudioEncoder.RET_SUCCESS:
                    ret_msg = self.encoder.get_last_error()
                    _log.error("Last error: " + ret_msg)
                    self._console_add("Last error: " + ret_msg)
            else:
                ret_msg = "WAV file does not exit for track \"" + sound_path + \
                          "\", and no alternative (xwm or mp3) was found.\n Try to use UnFuz first."
                _log.error("ret_msg:" + ret_msg)
                self._console_add("ret_msg:" + ret_msg)
        else:
            ret_flag = True
        if ret_val == SkyAudioEncoder.RET_SUCCESS:
            ret_flag = True
        # print([ret_flag, ret_val, ret_msg, ret_stdout])
        self._console_add(str([ret_flag, ret_val, ret_msg, ret_stdout]))
        return [ret_flag, ret_val, ret_msg, ret_stdout]

    def _console_clear(self):
        self.console_output = ""
        self._console_has_change = True

    def _console_add(self, msg: str):
        self.console_output += msg + "\n"
        self._console_has_change = True


from flask_restplus import Resource, Namespace, fields
from flask import request

from models.cloud.cloud_account import CloudAccount
from models.cloud.cloud_provider import CloudProvider
from models.general.customer import Customer
# Included Models for Marshalling
from apis.general.customers import model as cs
from apis.cloud.cloud_providers import model as cp

api = Namespace('cloud_accounts', description='Cloud Accounts')

model = api.model('cloud_accounts', {
    'uid': fields.String(required=True, description='Cloud Account ID'),
    'name': fields.String(required=True, description='Cloud Account Name'),
    'customer': fields.Nested(cs, "Customer"),
    'cloud_provider': fields.Nested(cp, "Cloud Provider")
})

model_cu = api.model('cloud_accounts_cu', {
    'uid': fields.String(required=True, description='Cloud Account ID'),
    'name': fields.String(required=True, description='Cloud Account Name'),
    'customer_uid': fields.String(required=True, description='Customer UID'),
    'cloud_provider_uid': fields.String(required=True, description='Cloud Provider UID')
})

@api.route('/')
class Cloud_Accounts(Resource):

    @api.marshal_list_with(model)
    def get(self):
        '''Lists all Cloud Accounts'''
        return list(CloudAccount.objects()), 200

    @api.expect(model_cu)
    @api.marshal_with(model_cu, code=201)
    def post(self):
        '''Creates a new Cloud Account'''

        if request.is_json:
            content = request.json

            customer = Customer.objects(uid=content["customer_uid"]).first()
            cloud_provider = CloudProvider.objects(uid=content["cloud_provider_uid"]).first()

            cloud_account = CloudAccount(uid=content["uid"], name=content["name"], \
                customer=customer, cloud_provider=cloud_provider)

            cloud_account.save()

            return cloud_account.to_dict(), 201

        else:
            return 400

@api.route('/<string:uid>')
class Cloud_Accounts_By_UID(Resource):

    @api.marshal_with(model)
    def get(self, uid):
        '''Shows a Cloud Account'''
        cloud_account = CloudAccount.objects(uid=uid).first()

        if cloud_account:
            return cloud_account, 200
        else:
            return {}, 404

    def delete(self, uid):
        '''Deletes a Cloud Account'''

        cloud_account = CloudAccount.objects(uid=uid)

        if cloud_account:
            cloud_account.delete()
            return {"msg": "{} has been removed.".format(uid)}, 200
        else:
            return {"msg": "{} has not been found.".format(uid)}, 404

    @api.expect(model_cu)
    @api.marshal_with(model_cu, code=200)
    def put(self, uid):
        '''Updates a Cloud Account'''
        if request.is_json:
            content = request.json
            cloud_account = CloudAccount.objects(uid=uid).first()
            if cloud_account:

                customer = Customer.objects(uid=content["customer_uid"]).first()
                cloud_provider = CloudProvider.objects(uid=content["cloud_provider_uid"]).first()

                cloud_account.name = content["name"]
                cloud_account.customer = customer
                cloud_account.cloud_provider = cloud_provider
                cloud_account.save()

                return cloud_account.to_dict(), 200

            else:

                return {}, 404

        else:

            return {}, 400


#!/usr/bin/env python
from traits.api import (HasTraits,  Array,  File, cached_property,
          Bool, Enum, Instance, on_trait_change, Property,
          DelegatesTo, Int, Button, List, Set, Float, Str,Directory)
import os
# Needed for Tabular adapter
from meap.gui_tools import (Item,HGroup,VGroup, HSplit, ObjectColumn,
                           TableEditor, ProgressDialog)
from traitsui.menu import OKButton, CancelButton
import numpy as np
import time
from meap.gui_tools import MEAPView, messagebox
from meap.io import load_from_disk, PhysioData
from glob import glob
import pandas as pd

import logging
logger = logging.getLogger(__name__)

class FileToProcess(HasTraits):
    input_file = File()
    output_file = File()
    finished = Bool(False)

    def _output_file_changed(self):
        self.finished = os.path.exists(self.output_file)

class PhysioFileColumn(ObjectColumn):
    def get_cell_color(self,object):
        if object.finished: return "light blue"
        return

files_table = TableEditor(
    columns = [
        PhysioFileColumn(name="input_file", width=1.0,
                         editable=False,label="Input File"),
        PhysioFileColumn(name="output_file", width=1.0,
                         editable=False,label="Output File"),
        ],
    auto_size  = True
)

class BatchFileTool(HasTraits):

    # For saving outputs
    file_suffix = Str("_finished.mea.mat")
    input_file_extension = Enum(".mea.mat", ".acq", ".mat")
    overwrite = Bool(False)
    input_directory = Directory()
    output_directory = Directory()
    files = List(Instance(FileToProcess))
    spreadsheet_file = File(exists=False)
    b_save = Button("Save Spreadsheet")

    def _input_file_extension_changed(self):
        self._input_directory_changed()

    def _input_directory_changed(self):
        potential_files = glob(self.input_directory +"/*" + self.input_file_extension)
        potential_files = [f for f in potential_files if not \
                           f.endswith(self.file_suffix) ]

        # Check if the output already exists
        def make_output_file(input_file):
            return input_file[:-len(self.input_file_extension)] + self.file_suffix

        self.files = [
            FileToProcess(input_file = f, output_file=make_output_file(f)) \
            for f in potential_files
        ]

        # If no output directory is set, use the input directory
        if self.output_directory == '':
            self.output_directory = self.input_directory

    def _b_save_fired(self):
        def get_row():
            return {"file":"", "outfile":"", "weight":"",
                    "height_ft":"", "weight":"", "electrode_distance_front":"",
                    "electrode_distance_back":"", "electrode_distance_left":"",
                    "electrode_distance_right":"", "resp_max":"", "resp_min":"",
                    "in_mri":"", "control_base_impedance":""}
        rows = []
        for f in self.files:
            row = get_row()
            row['file'] = f.input_file
            row['outfile'] = f.output_file
            rows.append(row)
        df = pd.DataFrame(rows)
        logger.info("Writing spreadsheet to %s",  self.spreadsheet_file)
        df.to_excel(self.spreadsheet_file, index=False)

    mean_widgets =VGroup(
            Item("input_file_extension"),
            Item("input_directory"),
            Item("output_directory"),
            Item("file_suffix"),
            Item("spreadsheet_file"),
            Item("b_save",show_label=False)
    )

    traits_view = MEAPView(
        HSplit(
            Item("files",editor=files_table,show_label=False),
            mean_widgets),
        resizable=True,
        win_title="Create Batch Spreadsheet",
        width=800, height=700,
        buttons = [OKButton,CancelButton]
    )


"""
Joint Label Fusion algorithm
"""

__all__ = ['joint_label_fusion']

import os
import numpy as np

from tempfile import mktemp
import glob
import re

from .. import utils
from ..core import ants_image as iio
from ..core import ants_image_io as iio2

def joint_label_fusion(target_image, target_image_mask, atlas_list, beta=4, rad=2,
                        label_list=None, rho=0.01, usecor=False, r_search=3,
                        nonnegative=False, verbose=False):
    """
    A multiple atlas voting scheme to customize labels for a new subject.
    This function will also perform intensity fusion. It almost directly
    calls the C++ in the ANTs executable so is much faster than other
    variants in ANTsR.

    One may want to normalize image intensities for each input image before
    passing to this function. If no labels are passed, we do intensity fusion.
    Note on computation time: the underlying C++ is multithreaded.
    You can control the number of threads by setting the environment
    variable ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS e.g. to use all or some
    of your CPUs. This will improve performance substantially.
    For instance, on a macbook pro from 2015, 8 cores improves speed by about 4x.

    ANTsR function: `jointLabelFusion`

    Arguments
    ---------
    target_image : ANTsImage
        image to be approximated

    target_image_mask : ANTsImage
        mask with value 1

    atlas_list : list of ANTsImage types
        list containing intensity images

    beta : scalar
        weight sharpness, default to 2

    rad : scalar
        neighborhood radius, default to 2

    label_list : list of ANTsImage types (optional)
        list containing images with segmentation labels

    rho : scalar
        ridge penalty increases robustness to outliers but also makes image converge to average

    usecor : boolean
        employ correlation as local similarity

    r_search : scalar
        radius of search, default is 3

    nonnegative : boolean
        constrain weights to be non-negative

    verbose : boolean
        whether to show status updates

    Returns
    -------
    dictionary w/ following key/value pairs:
        `segmentation` : ANTsImage
            segmentation image

        `intensity` : ANTsImage
            intensity image

        `probabilityimages` : list of ANTsImage types
            probability map image for each label

    Example
    -------
    >>> import ants
    >>> ref = ants.image_read( ants.get_ants_data('r16'))
    >>> ref = ants.resample_image(ref, (50,50),1,0)
    >>> ref = ants.iMath(ref,'Normalize')
    >>> mi = ants.image_read( ants.get_ants_data('r27'))
    >>> mi2 = ants.image_read( ants.get_ants_data('r30'))
    >>> mi3 = ants.image_read( ants.get_ants_data('r62'))
    >>> mi4 = ants.image_read( ants.get_ants_data('r64'))
    >>> mi5 = ants.image_read( ants.get_ants_data('r85'))
    >>> refmask = ants.get_mask(ref)
    >>> refmask = ants.iMath(refmask,'ME',2) # just to speed things up
    >>> ilist = [mi,mi2,mi3,mi4,mi5]
    >>> seglist = [None]*len(ilist)
    >>> for i in range(len(ilist)):
    >>>     ilist[i] = ants.iMath(ilist[i],'Normalize')
    >>>     mytx = ants.registration(fixed=ref , moving=ilist[i] ,
    >>>         typeofTransform = ('Affine') )
    >>>     mywarpedimage = ants.apply_transforms(fixed=ref,moving=ilist[i],
    >>>             transformlist=mytx['fwdtransforms'])
    >>>     ilist[i] = mywarpedimage
    >>>     seg = ants.threshold_image(ilist[i],'Otsu', 3)
    >>>     seglist[i] = ( seg ) + ants.threshold_image( seg, 1, 3 ).morphology( operation='dilate', radius=3 )
    >>> r = 2
    >>> pp = ants.joint_label_fusion(ref, refmask, ilist, r_search=2,
    >>>                     label_list=seglist, rad=[r]*ref.dimension )
    >>> pp = ants.joint_label_fusion(ref,refmask,ilist, r_search=2, rad=[r]*ref.dimension)
    """
    segpixtype = 'unsigned int'
    if (label_list is None) or (np.any([l is None for l in label_list])):
        doJif = True
    else:
        doJif = False

    if not doJif:
        if len(label_list) != len(atlas_list):
            raise ValueError('len(label_list) != len(atlas_list)')
        inlabs = np.sort(np.unique(label_list[0][target_image_mask != 0 ]))
        mymask = target_image_mask.clone()
    else:
        mymask = target_image_mask

    osegfn = mktemp(prefix='antsr', suffix='myseg.nii.gz')
    #segdir = osegfn.replace(os.path.basename(osegfn),'')

    if os.path.exists(osegfn):
        os.remove(osegfn)

    probs = mktemp(prefix='antsr', suffix='prob%02d.nii.gz')
    probsbase = os.path.basename(probs)
    tdir = probs.replace(probsbase,'')
    searchpattern = probsbase.replace('%02d', '*')

    mydim = target_image_mask.dimension
    if not doJif:
        # not sure if these should be allocated or what their size should be
        outimg = label_list[1].clone(segpixtype)
        outimgi = target_image * 0

        outimg_ptr = utils.get_pointer_string(outimg)
        outimgi_ptr = utils.get_pointer_string(outimgi)
        outs = '[%s,%s,%s]' % (outimg_ptr, outimgi_ptr, probs)
    else:
        outimgi = target_image * 0
        outs = utils.get_pointer_string(outimgi)

    mymask = mymask.clone(segpixtype)
    if (not isinstance(rad, (tuple,list))) or (len(rad)==1):
        myrad = [rad] * mydim
    else:
        myrad = rad

    if len(myrad) != mydim:
        raise ValueError('path radius dimensionality must equal image dimensionality')

    myrad = 'x'.join([str(mr) for mr in myrad])
    vnum = 1 if verbose else 0
    nnum = 1 if nonnegative else 0

    myargs = {
        'd': mydim,
        't': target_image,
        'a': rho,
        'b': beta,
        'c': nnum,
        'p': myrad,
        'm': 'PC',
        's': r_search,
        'x': mymask,
        'o': outs,
        'v': vnum
    }

    kct = len(myargs.keys())
    for k in range(len(atlas_list)):
        kct += 1
        myargs['g-MULTINAME-%i' % kct] = atlas_list[k]
        if not doJif:
            kct += 1
            castseg = label_list[k].clone(segpixtype)
            myargs['l-MULTINAME-%i' % kct] = castseg

    myprocessedargs = utils._int_antsProcessArguments(myargs)

    libfn = utils.get_lib_fn('antsJointFusion')
    rval = libfn(myprocessedargs)
    if rval != 0:
        print('Warning: Non-zero return from antsJointFusion')

    if doJif:
        return outimgi

    probsout = glob.glob(os.path.join(tdir,'*'+searchpattern ) )
    probsout.sort()
    probimgs = [iio2.image_read(probsout[0])]
    for idx in range(1, len(probsout)):
        probimgs.append(iio2.image_read(probsout[idx]))

    segmat = iio2.images_to_matrix( probimgs, target_image_mask )
    finalsegvec = segmat.argmax( axis = 0 )
    finalsegvec2 = finalsegvec.copy()
    # mapfinalsegvec to original labels
    for i in range(finalsegvec.max()+1):
        finalsegvec2[finalsegvec==i] = inlabs[i]
    outimg = iio2.make_image( target_image_mask, finalsegvec2 )

    return {
        'segmentation': outimg,
        'intensity': outimgi,
        'probabilityimages': probimgs
    }


'''
逻辑回归练习
1. 载入数据
2. 数据处理，图集降维，特征规范化
3. 随机初始化参数
4. 计算代价函数【正向传播】，并计算梯度【反向传播】，
5. 迭代更新参数 【梯度下降】
6. 模型评估
激活函数：sigmoid函数
算法的评估：留一法，k折法，自助法
梯度下降： 小批次梯度下降
使用正则化方法
采用训练集，交叉验证集，测试集的划分数据方式
'''
import numpy as np
import matplotlib.pyplot as plt
import h5py   #是与H5文件中存储的数据集进行交互的常用软件包
from lr_utils import load_dataset
import random
#加载数据集和测试集
train_set_x_orig , train_set_y , test_set_x_orig , test_set_y , classes  = load_dataset()

m_train  = train_set_y.shape[1]  #训练集的规模，图片数
m_test  = test_set_y.shape[1]    #测试集的规模
num_px = train_set_x_orig.shape[1]   #图片的规模，大小均为64*64
"""
#现在看一看我们加载的东西的具体情况
print ("训练集的数量: m_train = " + str(m_train))
print ("测试集的数量 : m_test = " + str(m_test))
print ("每张图片的宽/高 : num_px = " + str(num_px))
print ("每张图片的大小 : (" + str(num_px) + ", " + str(num_px) + ", 3)")
print ("训练集_图片的维数 : " + str(train_set_x_orig.shape))
print ("训练集_标签的维数 : " + str(train_set_y.shape))
print ("测试集_图片的维数: " + str(test_set_x_orig.shape))
print ("测试集_标签的维数: " + str(test_set_y.shape))
"""
#要把numpy数组(a,b,c,d)规模转化为（b*c*d,a),即使用reshape的-1参数，也就是从维数和其他参数中推出剩余参数
train_set_x_flatten  = train_set_x_orig.reshape((train_set_x_orig.shape[0],-1)).T
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0],-1).T
#print('降维之后的测试集：'+str(test_set_x_flatten.shape))

#标准化数据集，特征规范化归一化处理,因为图像数据都是0到255的数据，所以除以255让数据在0到1之内
train_set_x = train_set_x_flatten[:,0:150] / 255
verify_set_x = train_set_x_flatten[:,150:209]/255
test_set_x = test_set_x_flatten / 255

verify_set_y  = train_set_y[:,150:209]
train_set_y  = train_set_y[:,0:150]
#计算激活函数 ，sigmond函数
def sigmoid(z):
    '''参数：z-任何大小的标量或者numpy数组。实际上z= W.T*x+b
    返回值：s = sigmoid(z)'''
    s = 1 / ( 1+ np.exp(-z))
    return s

'''
#测试激活函数
a = np.array([[0,1,9.2,-9.2,100]])
print('sigmoid(a):'+str(sigmoid(a)))
print('log(z):'+str(np.log(sigmoid(a))))
exit()
'''
#初始化参数
def initialize_with_zeros(dim):
    '''此函数为w创建一个维度为（dim,1）的随机向量，并把b初始化为1
    参数： dim  -  我们想要的w矢量的大小（或者这种情况下的参数数量）
    返回：  w-维度为（dim，1）的随机向量，b-初始化的标量，对应于偏差'''
    w = np.random.rand(dim, 1)*0.01+0.0001
    b = 0
    # 使用断言来确保我要的数据是正确的
    assert (w.shape == (dim, 1))  # w的维度是(dim,1)
    assert (isinstance(b, float) or isinstance(b, int))  # b的类型是float或者是int

    return (w,b)

#前向传播和后向传播，计算代价函数和梯度
def propagate(w,b,X,Y,Lambda):
    '''实现前向和后向传播的成本函数及其梯度
    参数：
    w  - 权重，大小不等的数组（num_px * num_px * 3 ,1）
    b  - 偏差，一个标量
    X  - 矩阵类型为（num_px*num_px*3,训练数量）
    Y  - 真正的标签矢量，即监督的正确答案
    Lambda - 正则化系数，是一个超参数
    返回：cost: 逻辑回归的负对数似然成本
          dw :  相对于w的损失梯度
          db：  相对于b的损失梯度'''
    #使用小批次梯度下降,批次为32
    m  = 32
    random_index = random.randint(0,X.shape[1]-m)
    x =X[:,random_index:random_index+m]
    y =Y[:,random_index:random_index+m]
    #正向传播
    A = sigmoid(np.dot(w.T,x)+b) #计算激活函数值，即y
    cost  = (- 1 / m) * np.sum(y * np.log( A )+ ( 1 - y ) *(np.log( 1 - A ))) #计算代价函数值
    #添加正则化项
    cost += (Lambda/(2*m))*np.sum(w * w)
    #反向传播
    #print(x.shape,y.shape,w.shape)
    dw = (1 / m) * np.dot(x,( A - y ).T) + (Lambda / m)* w
    db = (1 / m) * np.sum( A - y)

    # 使用断言确保我的数据是正确的
    assert (dw.shape == w.shape)
    assert (db.dtype == float)
    cost = np.squeeze(cost) #降维，变成数
    assert (cost.shape == ())

    #创建字典，保存梯度
    grads  = {'dw':dw,'db':db}

    return (grads ,cost)
'''
#测试传播过程
print('===========test  propagate ===============')
#初始化参数
w,b,X,Y = np.array([[1],[2]]),2,np.array([[1,2],[3,4]]),np.array([[1,0]])
#2个样本，2个特征值
grads ,cost  =  propagate(w,b,X,Y)
print('dw='+str(grads['dw']))
print('db='+str(grads['db']))
print('cost='+str(cost))
'''

#更新参数，梯度下降
def optimize(w,b,X,Y,Lambda, num_iterations, learning_rate, print_cost =False):
    '''
    此函数通过运行梯度下降算法来优化w和b
    参数：
      w  -权重，和样本特征值规模相当
      b  -偏差，一个标量
      X  - 维度为（num_px*num_px*3,训练数据的数量）的数组
      Y  - 真正的’标签’矢量
      Lambda - 正则化系数
      num_iterations  -优化循环的迭代次数
      learning_rate    -梯度下降更新的学习率
      print_cost   -每一千步打印一次损失值，绘图

    返回：
      params   - 包含权重w和偏差b的字典
      grads    - 包含权重w和偏差下降梯度的字典
      costs    - 成本-优化期计算的所有成本列表，用于绘制学习曲线
    '''
    costs = []
    for i in range(num_iterations):
        grads,cost =propagate(w,b,X,Y,Lambda)

        dw = grads['dw']
        db = grads['db']
        #更新参数
        w = w - learning_rate * dw
        b = b - learning_rate * db

        #记录成本
        if i%1000 == 0:
            costs.append(cost)
        #打印成本数据
        if (print_cost) and (i%1000 == 0) :
            print('迭代的次数：%i,误差值：%f'%(i,cost))
        #用字典记录参数和梯度
        params  ={'w':w,'b':b}
        grads   = {'dw':dw,'db':db}

    return (params,grads,costs)
'''
#测试optimize
print("====================测试optimize====================")
w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1,2], [3,4]]), np.array([[1, 0]])
params , grads , costs = optimize(w , b , X , Y , num_iterations=100 , learning_rate = 0.009 , print_cost = False)
print ("w = " + str(params["w"]))
print ("b = " + str(params["b"]))
print ("dw = " + str(grads["dw"]))
print ("db = " + str(grads["db"]))
'''

#输出预测值
def predict(w,b,X):
    '''
    使用学习逻辑回归参数预测 标签
    :param w:  权重
    :param b:  偏差
    :param X:   训练集数据
    :return:   Y_prediction 包含X中所有图片的所有预测
    '''
    m  = X.shape[1]  #图片的数量
    Y_prediction  = np.zeros((1,m))
    w = w.reshape(X.shape[0],1)

    #记预测猫在图片中出现的概率
    A = sigmoid(np.dot(w.T ,X)+b)
    for i in range(A.shape[1]):
        if A[0,i] > 0.5:
            Y_prediction[0,i] = 1
        else:
            Y_prediction[0,i] = 0
    assert(Y_prediction.shape == (1,m))

    return Y_prediction
'''
#测试predict
print("====================测试predict====================")
w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1,2], [3,4]]), np.array([[1, 0]])
print("predictions = " + str(predict(w, b, X)))'''

def model(X_train,Y_train,X_test,Y_test,X_verify,Y_verify, learning_rate,Lambda,num_iterations=2000,print_cost =False):
    '''
    通过调用之间实现的函数来构建逻辑回归模型
    :param X_train: 训练集
    :param Y_train: 训练标签集
    :param X_test: 测试集
    :param Y_test:  测试标签集
    :param num_iterations: 迭代次数，超参数
    :param learning_rate:   学习率，超参数
    :param print_cost:   设置为true以每100次迭代打印成本
    :return: d--包含有关模型信息的字典
    '''
    #初始化参数
    w ,b =  initialize_with_zeros(X_train.shape[0])
    #通过学习得到参数，梯度和损失
    parameters , grads , costs = optimize(w,b,X_train,Y_train,Lambda,
                                          num_iterations,learning_rate,print_cost)

    #从字典参数中检索参数w和b
    w,b = parameters['w'],parameters['b']

    #预测测试集/训练集的例子
    Y_prediction_test = predict(w, b, X_test)
    Y_prediction_verify = predict(w,b,X_verify)
    Y_prediction_train = predict(w, b, X_train)
    # 打印训练后的准确性
    print("训练集准确性：", format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100), "%")
    print("交叉验证集准确性：", format(100 - np.mean(np.abs(Y_prediction_verify - Y_verify)) * 100), "%")
    print("测试集准确性：", format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100), "%")
    correct = 1 - np.mean(np.abs(Y_prediction_verify - Y_verify))

    d = {
        "costs": costs,
        "Y_prediction_test": Y_prediction_test,
        "Y_prediciton_train": Y_prediction_train,
        "w": w,
        "b": b,
        "learning_rate": learning_rate,
        "num_iterations": num_iterations,
         "correct":correct}
    return d
print("====================测试model====================")

Lambda =12
d = model(train_set_x, train_set_y, test_set_x, test_set_y,verify_set_x,verify_set_y,learning_rate=0.007,
                   Lambda=0.5, num_iterations = 20000,  print_cost = True)

#绘制图
costs = np.squeeze(d['costs'])
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('iterations (per thousands)')
plt.title("Learning rate =" + str(d["learning_rate"])+"\nlambda ="+str(Lambda))
plt.show()


import matplotlib.pyplot as plt
import math
import statistics
# import matplotlib.pyplot as plt
import numpy as np
from matplotlib import colors
# from matplotlib.ticker import PercentFormatter

# f=open('GSE79578_bulkRNAseq.txt')
# new_f=open('GSE79578_bulkRNAseq_96h.bed','a')
# # for line in f.readlines(3):
# #     l=line.strip().split()
# # #     for i,j in enumerate(l):
# # #         print(i,j)
# for line in f.readlines():
#     L = line.strip().split()
# #     # print(L[0],L[10])
#     new_f.write(L[0]+'\t'+L[10]+'\n')

def adding_mathyalation_to_genes_peek_file():
    genes_peek_file=open('genes_peeks_4d_RA_H3K36me3_Intersect.ChIP.peaks.CpG.bed_.bed')
    new_peek_genes_file=open('genes_peeks_4d_RA_H3K36me3_Intersect.ChIP.peaks.CpG.bed','a')
    methyl_file=open('new_4d_RA_H3K36me3Intersect.ChIP.peaks.CpG.bed')

    # genes_peek_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.bed_.bed')
    # new_peek_genes_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.bed', 'a')
    # methyl_file = open('new_4d_RA_H3K36me3Intersect.WCE.ChIP.peaks.CpG.bed')

    # genes_peek_file = open('genes_peeks_no_2i_2d_H3K36me3_Bismark_PE_Intersect.WCE.ChIP.peaks.CpG.bed_.bed')
    # new_peek_genes_file = open('genes_peeks_no_2i_2d_H3K36me3_Bismark_PE_Intersect.WCE.ChIP.peaks.CpG.bed', 'a')
    # methyl_file = open('new_no_2i_2d_H3K36me3_Bismark_PEIntersect.WCE.ChIP.peaks.CpG.bed')
    #
    # genes_peek_file = open('genes_peeks_no_2i_2d_H3K36me3_Bismark_PE_Intersect.ChIP.peaks.CpG.bed_.bed')
    # new_peek_genes_file = open('genes_peeks_no_2i_2d_H3K36me3_Bismark_PE_Intersect.ChIP.peaks.CpG.bed', 'a')
    # methyl_file = open('new_no_2i_2d_H3K36me3_Bismark_PEIntersect.ChIP.peaks.CpG.bed')

    methyl_percent={}
    peek_methyl_percent={}
    peeks={}
    for line in methyl_file:
        L = line.strip().split()
        peek=L[0]
        methyl=L[1]
        non_methyl=L[2]
        reads=L[3]
        percent=L[4]
        methyl_percent[peek]=(peek,methyl,non_methyl,reads,percent)
    for line_peek in genes_peek_file.readlines():
        L_peek = line_peek.strip().split()
        peek=L_peek[2]
        peeks[peek]=(L_peek[0],L_peek[1],L_peek[2],L_peek[3],L_peek[4],L_peek[5])
    # print(peeks)
    for peek,i in peeks.items():

        for peek_percent_m,j in methyl_percent.items():
            if peek==peek_percent_m:
                if peek not in peek_methyl_percent:
                    peek_methyl_percent[peek]=(i[0],i[1],i[2],i[3],i[4],i[5],j[1],j[2],j[3],j[4])
    for peek,v in peek_methyl_percent.items():
        new_peek_genes_file.write(v[0]+'\t'+v[1]+'\t'+v[2]+'\t'+v[3]+'\t'+v[4]+'\t'+v[5]+'\t'+v[6]+'\t'+v[7]+'\t'+v[8]+'\t'+v[9]+'\n')
    # print(peek_methyl_percent)
# print(adding_mathyalation_to_genes_peek_file())

def met_vs_exp(): ###################################################
    # methylation_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.ChIP.peaks.CpG.bed')
    # gene_exp_file = open('GSE79578_bulkRNAseq_96h.bed')
    # methyl_vs_exp = open('genes_peeks_4d_RA_H3K36me3_Intersect.ChIP.peaks.CpG.metVSexp.bed', 'a')

    methylation_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.bed')
    gene_exp_file = open('GSE79578_bulkRNAseq_96h.bed')
    methyl_vs_exp = open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.metVSexp.bed', 'a')

    met_dict = {}
    exp_dict = {}
    for line in methylation_file.readlines():
        L = line.strip().split()
        gene_name=L[1]
        met=float(L[6])
        non_met=float(L[7])
        if gene_name not in met_dict:
            met_dict[gene_name]=(met,non_met)
        else:
            methyl,non_methyl=met_dict[gene_name]
            met_dict[gene_name]=(float(methyl+met),float(non_methyl+non_met))
    for exp_line in gene_exp_file.readlines():
        exp_L= exp_line.strip().split()
        gene_name_exp=exp_L[0]
        gene_exp=exp_L[1]
        exp_dict[gene_name_exp]=gene_exp
    for gene_name in met_dict:
        me,non=met_dict[gene_name]
        if gene_name in exp_dict.keys():
            methyl_vs_exp.write(gene_name+'\t'+exp_dict[gene_name]+'\t'+str(me)+'\t'+str(non)+'\t'+(str(float(me)/(float(me+non))))+'\n')
# print(met_vs_exp())

def chip_nimus_wce():
    chip_methyl_vs_exp_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.ChIP.peaks.CpG.metVSexp.bed')
    wce_methyl_vs_exp_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.metVSexp.bed')
    files=[chip_methyl_vs_exp_file,wce_methyl_vs_exp_file]
    chip_nimus_wce_file=open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.metVSexp_delta.bed','a')
    chip_nimus_wce_file.write(
        'gene' + '\t' + 'exp_wce' + '\t' + 'met_wce' + '\t' + 'non_met_wce' + '\t' + 'met_percent_wce' + '\t' +
        'exp_chip' + '\t' + 'met_chip' + '\t' + 'non_met_chip' + '\t' + 'met_percent_chip' + '\t' +
        'met_percent_chip - met_percent_wce' + '\n')
    chip_methyl_vs_exp_dict={}
    wce_methyl_vs_exp_dict = {}
    for file in files:
        print(file.name)
        dictionary={}
        for line in file:
            L=line.strip().split()
            gene_name=L[0]
            exp=L[1]
            met=L[2]
            non_met=L[3]
            met_percent=L[4]
            dictionary[gene_name]= (exp,met,non_met,met_percent)

        if 'WCE' not in file.name:
            print(dictionary)
            chip_methyl_vs_exp_dict=dictionary
            print(chip_methyl_vs_exp_dict)
        elif 'WCE' in file.name:
            wce_methyl_vs_exp_dict=dictionary
    for gene in chip_methyl_vs_exp_dict:
        if gene in wce_methyl_vs_exp_dict:
            exp_wce,met_wce,non_met_wce,met_percent_wce=wce_methyl_vs_exp_dict[gene]
            exp_chip,met_chip,non_met_chip,met_percent_chip=chip_methyl_vs_exp_dict[gene]
            chip_nimus_wce_file.write(gene+'\t'+exp_wce+'\t'+met_wce+'\t'+non_met_wce+'\t'+met_percent_wce+'\t'+
                                      exp_chip+'\t'+ met_chip+'\t'+ non_met_chip+'\t'+ met_percent_chip+'\t'+
                                      str(float(met_percent_chip)-float(met_percent_wce))+'\n')

# print(chip_nimus_wce())
def create_plots_expression_vs_methylation():
    chip_methyl_vs_exp_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.ChIP.peaks.CpG.metVSexp.bed')
    wce_methyl_vs_exp_file = open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.metVSexp.bed')
    chip_minus_wce_file=open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.metVSexp_delta.bed')
    files=[chip_methyl_vs_exp_file,wce_methyl_vs_exp_file,chip_minus_wce_file]
    for file in files:
        exp_y=[]
        met_x=[]
        for line in file.readlines():
            L=line.strip().split()
            if len(L)>=7:
                exp=math.log2(float(L[1])+1)
                # exp=float(L[1])
                exp_y.append(float(exp))
                met_x.append(float(L[9]))
            else:
                exp = math.log2(float(L[1]) + 1)
                # exp = float(L[1])
                exp_y.append(float(exp))
                met_x.append(float(L[4]))
        #plt.hist(met_x,bins=5)
        #plt.show()
        plt.title(file.name)
        plt.plot(met_x,exp_y, 'k.')
        plt.show()

create_plots_expression_vs_methylation()

# print(create_plots(0,1,new_list))

# file=open('genes_peeks_4d_RA_H3K36me3_Intersect.WCE.ChIP.peaks.CpG.metVSexp_delta.bed')
# x=[]
# for line in file.readlines():
#     L=line.strip().split()
#     x.append(L[9])
# print(x)
#
# fig, axs = plt.subplots(1, 2,sharey=True, tight_layout=True)
# axs[0].hist(x)
# axs[1].hist(x, bins=20)
# plt.show()


from item import Weapon
import pygame

# Inventory class. For keeping track of who has what.
class Inventory:

    def __init__(self):
        self.items = []
        self.main_hand = Weapon(pygame.image.load("Image_Assets\\meter_1.png"), 0, 0, 0)
        self.off_hand = Weapon(pygame.image.load("Image_Assets\\meter_1.png"), 0, 0, 0)

    def add_item(self, item, quantity=1):
        for i in self.items:
            if i[0] == item.name:
                i[1] += quantity
                return
        self.items.append([item.name, quantity])

    def use_item(self, hud, item, quantity=1):
        for i in self.items:
            if i[0] == item.name:
                if i[1] >= quantity:
                    i[1] -= quantity
                    item.use(hud, quantity)
                else:
                    print('Error: Not enough items to use')
                return

    def set_main_hand(self, item):
        self.main_hand = item
        return

    def set_off_hand(self, item):
        self.off_hand = item