# chat/consumers.py
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from rest_framework import serializers
from ..models import Message, Room
from ..serializers import MessageSerializer

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        self.room, created = Room.objects.get_or_create(label=self.room_name)

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )

        self.accept()

    def disconnect(self, close_code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        get_all_messages = text_data_json.get("get_all_messages")

        if get_all_messages == True:
            async_to_sync(self.channel_layer.group_send)(
                self.room_group_name,
                {
                    'type': 'all_messages',
                }
            )
        else:
            # Save message to DB
            message = text_data_json['message']
            message_object = Message(room=self.room, message=message)
            message_object.save()

            # Send message to room group
            async_to_sync(self.channel_layer.group_send)(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': message
                }
            )

    # Receive request get all messages
    def get_all_messages(self):
        all_messages = Message.objects.filter(room=self.room)
        all_messages_data = MessageSerializer(all_messages, many=True).data
        return all_messages_data

    def all_messages(self, event):
        result_messages = {
            'all_messages': self.get_all_messages(),
            'send_all_messages': True
        }
        self.send(text_data=json.dumps(result_messages))

    # Receive message from room group
    def chat_message(self, event):
        message = event['message']

        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'message': message
        }))


from datetime import datetime, timezone
import os
from pathlib import Path
import tempfile
import threading
import time
import tkinter
from urllib.error import HTTPError
import webbrowser
from PIL.ImageTk import PhotoImage
from hurry.filesize import size
from tkinter.ttk import *
from tkinter import filedialog, ttk
import psutil as psutil
from urllib.request import urlopen
import tqdm
import zipfile
import sys
import json

current_version = 'v0.7'
installer_release_url = 'https://api.github.com/repos/externoak/A32NX-installer/releases/latest'
master_prerelease_url = 'https://api.github.com/repos/flybywiresim/a32nx/releases/tags/vmaster'
latest_release_url = 'https://api.github.com/repos/flybywiresim/a32nx/releases/latest'
asset_json_name = 'asset.json'
active_download_server = 'Github'

class Request:

    cancel_check = False

    @staticmethod
    def get(url: str):
        try:
            response = urlopen(url)
            return response
        except HTTPError as e:
            return e

    @staticmethod
    def download_file(url: str, file_name: str, progress_bar: Progressbar, response_status: ttk.Label, stable: bool, download_size: int = None):
        request.cancel_check = False
        if stable:
            download_message = f"Downloading stable version..."
        else:
            download_message = f"Downloading development version..."
        chunk_size = 524288
        dl = 0
        try:
            req = urlopen(url)
            if req.status == 200:
                start = time.perf_counter()
                try:
                    progress_bar.pack(side="top", pady=(40, 0))
                    progress_bar['value'] = 0
                    file_size = int(req.headers['Content-Length'])
                    pbar_percentage = tqdm.tqdm(total=int(file_size), bar_format='{percentage}')
                    with open(f'{sys.prefix}/{file_name}', 'wb') as fp:
                        response_status['text'] = download_message
                        while True:
                            chunk = req.read(chunk_size)
                            if not chunk:
                                break
                            fp.write(chunk)
                            dl += len(chunk)
                            response_status['text'] = f"{download_message} {round(dl // (time.perf_counter() - start) / 1600000, 3)} MB/s"
                            pbar_percentage.update(len(chunk))
                            current_percentage = round(float(str(pbar_percentage)))
                            progress_bar['value'] = current_percentage
                            style.configure('text.Horizontal.TProgressbar', text=f'{current_percentage} %')
                            progress_bar.update()
                            if request.cancel_check:
                                fp.close()
                                break
                            fp.flush()
                    progress_bar.pack_forget()
                    response_status['text'] = ""
                except TypeError:
                    progress_bar.pack_forget()
                    total_chunk_downloaded = 0
                    with open(f'{sys.prefix}/{file_name}', 'wb') as fp:
                        while True:
                            chunk = req.read(chunk_size)
                            if not chunk:
                                break
                            total_chunk_downloaded = total_chunk_downloaded + chunk_size
                            response_status['text'] = f"{download_message} {round(dl // (time.perf_counter() - start) / 1600000, 3)} MB/s Total downloaded: {size(total_chunk_downloaded)}b!"
                            fp.write(chunk)
                            dl += len(chunk)
                            progress_bar.update()
                            if request.cancel_check:
                                fp.close()
                                break
                            fp.flush()
                    response_status['text'] = ""
                except PermissionError:
                    response_status['text'] = f"Error when downloading, permission denied. Please try and run as admin."
                    response_status['background'] = "firebrick"
            elif req.status == 302:
                request.download_file(url=req.headers['location'], file_name=file_name, progress_bar=progress_bar, response_status=response_status, stable=stable, download_size=download_size)
        except HTTPError as e:
            response_status['text'] = f"Error when downloading, response code: {e}"
            response_status['background'] = "firebrick"
        progress_bar.pack_forget()

    @staticmethod
    def update_cancel():
        request.cancel_check = True

    @staticmethod
    def open_installer_release_page_browser():
        webbrowser.open(url='https://github.com/Externoak/A32NX-installer/releases/latest')

    @staticmethod
    def open_installer_faqs_page_browser(_self):
        webbrowser.open(url='https://github.com/Externoak/A32NX-installer#faqs')

class CreateToolTip(object):

    def __init__(self, widget, text='widget info'):
        self.wait_time = 500
        self.wrap_length = 180
        self.widget = widget
        self.text = text
        self.widget.bind("<Enter>", self.enter)
        self.widget.bind("<Leave>", self.leave)
        self.widget.bind("<ButtonPress>", self.leave)
        self.id = None
        self.tw = None

    def enter(self, _event=None):
        self.schedule()

    def leave(self, _event=None):
        self.unschedule()
        self.hidetip()

    def schedule(self):
        self.unschedule()
        self.id = self.widget.after(self.wait_time, self.showtip)

    def unschedule(self):
        current_id = self.id
        self.id = None
        if current_id:
            self.widget.after_cancel(current_id)

    def showtip(self):
        x, y, cx, cy = self.widget.bbox("insert")
        if str(self.widget) == ".!label6":
            x += self.widget.winfo_rootx() + 300
            y += self.widget.winfo_rooty() - 20
        else:
            x += self.widget.winfo_rootx() + 50
            y += self.widget.winfo_rooty() + 35
        self.tw = tkinter.Toplevel(self.widget)
        self.tw.wm_overrideredirect(True)
        self.tw.wm_geometry("+%d+%d" % (x, y))
        label = ttk.Label(self.tw, text=self.text, justify='left',
                          background="#1B1B1B", relief='solid', borderwidth=1,
                          wraplength=self.wrap_length)
        label.pack(ipadx=1)

    def hidetip(self):
        tw = self.tw
        self.tw = None
        if tw:
            tw.destroy()

class Application(ttk.Frame):

    def __init__(self, master=None):
        super().__init__(master)
        master.title(f'FlyByWire Downloader {current_version}')
        self.change_folder = True
        self.user_tmp_directory = tempfile.gettempdir()
        self.original_background = ""
        self.Artwork = ttk.Label(image="", borderwidth=0)
        self.photo = PhotoImage(file=f"{sys.prefix}/Image.pbm")
        self.Artwork['image'] = self.photo
        self.Artwork.photo = self.photo
        self.Artwork.pack()
        if "FlightSimulator.exe" not in (p.name() for p in psutil.process_iter()):
            self.latest_release_version = ""
            self.development_github_data = {}
            self.latest_development_update_timestamp = ""
            self.response_status = ttk.Label(text="Welcome to A32NX Mod Downloader & Installer!", wraplength=400, background="#1B1B1B", foreground="white")
            self.response_status.pack(side="top", fill=tkinter.X)
            self.update_installer_label = ttk.Label(text="", background="#1B1B1B")
            self.filler_label = ttk.Label(text="", background="#1B1B1B")
            self.filler_label.pack(side="top", fill=tkinter.X)
            self.destination_folder_msg = ttk.Label(text="", background="#1B1B1B", wraplength=400)
            self.destination_folder_msg.pack(side="top", fill=tkinter.X)
            self.progress_bar = ttk.Progressbar(self, style='text.Horizontal.TProgressbar', orient="horizontal", length=200, mode="determinate")
            self.download_dev_btn = ttk.Button(self, cursor="hand2", text="Development version", width=20, style='W1.TButton', command=self.download_dev)
            self.download_stable_btn = ttk.Button(self, cursor="hand2", width=20, text="Stable version", style='W2.TButton', command=self.download_stable)
            self.browse_button = ttk.Button(self, text="", cursor="hand2", width=30, style='W3.TButton', command=self.browse_search)
            threading.Thread(target=self.check_installer_update).start()
            self.exit = ttk.Button(self, text="Exit", cursor="hand2", style='W4.TButton', command=self.master.destroy)
            self.help_label = ttk.Label(text="Help", cursor="hand2", wraplength=400, font=('Verdana', 8, 'bold', 'underline'), background="#1B1B1B", foreground="#ffc93c", anchor='e')
            self.help_label.bind("<Button-1>", request.open_installer_faqs_page_browser)
            CreateToolTip(self.help_label, "Click me to open FAQs")
            self.exit.pack(side="bottom", pady=(30, 0), padx=(184, 184))
            self.help_label.pack(side="bottom", fill=tkinter.BOTH)
            self.filler_label2 = ttk.Label(text="", background="#1B1B1B")
            self.filler_label2.pack(side="bottom", fill=tkinter.X)
            self.cancel = ttk.Button(self, cursor="hand2", text="Cancel", style='W5.TButton', command=request.update_cancel)
            root.bind_class("TButton", "<Enter>", self.on_enter)
            root.bind_class("TButton", "<Leave>", self.on_leave)
            try:
                self.user_cfg_path = None
                self.get_custom_file_path()
                if not self.user_cfg_path:
                    normal_steam_user_cfg_location = Path(f'{os.environ["APPDATA"]}\\Microsoft Flight Simulator\\UserCfg.opt')
                    normal_msfs_store_user_cfg_location = Path(f'{os.environ["LOCALAPPDATA"]}\\Packages\\Microsoft.FlightSimulator_8wekyb3d8bbwe\\LocalCache\\UserCfg.opt')
                    if normal_steam_user_cfg_location.is_file():
                        self.user_cfg_path = normal_steam_user_cfg_location
                    elif normal_msfs_store_user_cfg_location.is_file():
                        self.user_cfg_path = normal_msfs_store_user_cfg_location
                    else:
                        try:
                            for path in Path(Path(os.environ['APPDATA']).parent).rglob('UserCfg.opt'):
                                clean_path = path.resolve()
                                if "Flight" in str(clean_path) and str(clean_path).endswith('UserCfg.opt'):
                                    self.user_cfg_path = path
                                    break
                        except FileNotFoundError:
                            pass
                    if not self.user_cfg_path:
                        raise IOError
                    file_data = open(self.user_cfg_path, 'r')
                    found_installation_path = ""
                    for row in file_data:
                        if "InstalledPackagesPath" in row:
                            found_installation_path = row.split('InstalledPackagesPath')[1].lstrip().rstrip().strip('"')
                            self.destination_folder = f'{found_installation_path}\\Community'
                            self.destination_folder_msg['text'] = found_installation_path
                    if not found_installation_path:
                        raise IOError
                    file_data.close()
                else:
                    self.destination_folder = self.user_cfg_path
                    self.destination_folder_msg['text'] = self.user_cfg_path
                self.browse_button['text'] = "Change destination folder"
                self.browse_button.pack(side="top", pady=(20, 0))
                self.browse_search()
            except IOError:
                self.destination_folder = ""
                self.response_status['text'] = "Welcome to A32NX Mod Downloader & Installer. Could not automatically detect Community folder, please select it manually!"
                self.browse_button['text'] = "Select Community folder"
                self.browse_button.pack(side="top", pady=(20, 0))
        else:
            self.response_status = ttk.Label(text="Welcome to A32NX Mod Downloader & Installer! Please close MSFS before installing/updating the A32NX mod to avoid issues.", wraplength=400, background="firebrick", foreground="white")
            self.response_status.pack(side="top", fill=tkinter.X)
            self.exit = ttk.Button(self, text="Exit", style='W4.TButton', command=self.master.destroy)
            self.exit.pack(side="bottom", pady=(30, 0), padx=(184, 184))
        self.pack(after=self.exit.pack(side="bottom", pady=(20, 0), padx=(184, 184)))

    def get_asset_json_path(self) -> Path:
        return Path(f'{self.destination_folder}\\A32NX\\{asset_json_name}')

    def custom_folder_path(self) -> Path:
        return Path(f'{self.user_tmp_directory}\\CustomCommunityFolder.json')

    def get_custom_file_path(self):
        try:
            if self.custom_folder_path().is_file():
                with open(self.custom_folder_path()) as f:
                    custom_file_data = json.load(f)
                    self.user_cfg_path = Path(custom_file_data['path'])
        except json.decoder.JSONDecodeError:
            self.custom_folder_path().unlink()

    def set_custom_file_path(self):
        try:
            if self.destination_folder:
                data = {'path': self.destination_folder}
                with open(self.custom_folder_path(), 'w', encoding='utf-8') as f:
                    json.dump(data, f, ensure_ascii=False, indent=4)
        except IOError:
            pass

    def browse_search(self):
        self.response_status['background'] = ""
        self.response_status.pack(side="top", fill=tkinter.X)
        if not self.change_folder or not self.destination_folder:
            previous_folder = self.destination_folder
            self.destination_folder = filedialog.askdirectory()
            if not self.destination_folder and previous_folder:
                self.destination_folder = previous_folder
            if previous_folder != self.destination_folder:
                self.set_custom_file_path()
        if self.destination_folder:
            self.response_status['text'] = "Welcome to A32NX Mod Downloader & Installer!"
            threading.Thread(target=self.check_if_update_available).start()
            self.download_dev_btn.pack(side="left", pady=(20, 0), padx=(20, 0))
            self.download_stable_btn.pack(side="right", pady=(20, 0), padx=(0, 20))
            msg = f'Destination folder: {self.destination_folder}'
            self.browse_button['text'] = "Change destination folder"
            self.browse_button.pack(side="top", pady=(20, 0))
            self.change_folder = False
        else:
            self.filler_label['text'] = ""
            self.filler_label['background'] = "#1B1B1B"
            self.download_dev_btn.pack_forget()
            self.download_stable_btn.pack_forget()
            msg = 'Please select an installation folder.'
            self.browse_button['text'] = "Select Community folder"
            self.change_folder = True
            self.browse_button.pack(side="top", pady=(20, 0))
        self.destination_folder_msg['text'] = msg
        self.destination_folder_msg.pack(side="top", fill=tkinter.X)
        ttk.Label(root, text="name")

    def download_zip(self, specific_url: str, stable: bool = True):
        if self.destination_folder:
            download_url = None
            file_name = None
            if active_download_server == 'Github':
                response = request.get(specific_url)
                if response.status == 200:
                    download_url = json.load(response)["assets"][0]["browser_download_url"]
                    file_name = download_url.split("/")[-1]
            else:
                file_name = specific_url.split("/")[-1]
                download_url = specific_url
            if file_name and download_url:
                self.filler_label['text'] = ""
                self.filler_label['background'] = "#1B1B1B"
                self.browse_button.pack_forget()
                self.download_dev_btn.pack_forget()
                self.download_stable_btn.pack_forget()
                self.cancel.pack(side="bottom", pady=(20, 0), padx=(184, 184))
                self.exit.pack_forget()
                threading.Thread(target=request.download_file(url=download_url, file_name=file_name, progress_bar=self.progress_bar, response_status=self.response_status, stable=stable)).start()
            if not self.response_status['text'] and not request.cancel_check:
                threading.Thread(target=self.unzip_file, kwargs={'file_name': file_name, 'stable': stable}).start()
            elif request.cancel_check or "permission denied" in self.response_status['text'] or 'HTTP' in self.response_status['text']:
                if not self.response_status['text']:
                    self.response_status['text'] = f"Download cancelled!"
                self.exit.pack(side="bottom", pady=(20, 0), padx=(184, 184))
                self.cancel.pack_forget()
                self.browse_button.pack(side="top", pady=(20, 0))
                self.download_dev_btn.pack(side="left", pady=(20, 0), padx=(20, 0))
                self.download_stable_btn.pack(side="right", pady=(20, 0), padx=(0, 20))
            elif not file_name or not download_url:
                self.response_status['text'] = f"Error when downloading, could not fetch download data!"
                self.response_status['background'] = "firebrick"

    def download_stable(self):
        active_latest_release_url = latest_release_url
        if active_download_server == 'FlyByWire CDN':
            active_latest_release_url = 'https://flybywiresim-packages.nyc3.cdn.digitaloceanspaces.com/stable/A32NX-stable.zip'
        elif active_download_server == 'FlyByWire non CDN':
            active_latest_release_url = 'https://flybywiresim-packages.nyc3.digitaloceanspaces.com/stable/A32NX-stable.zip'
        self.download_zip(specific_url=active_latest_release_url)

    def download_dev(self):
        active_master_prerelease_url = master_prerelease_url
        if active_download_server == 'FlyByWire CDN':
            active_master_prerelease_url = 'https://flybywiresim-packages.nyc3.cdn.digitaloceanspaces.com/vmaster/A32NX-master.zip'
        elif active_download_server == 'FlyByWire non CDN':
            active_master_prerelease_url = 'https://flybywiresim-packages.nyc3.digitaloceanspaces.com/vmaster/A32NX-master.zip'
        self.download_zip(specific_url=active_master_prerelease_url, stable=False)

    def check_installer_update(self):
        try:
            latest_installer_version = json.load(request.get(installer_release_url))['tag_name']
            if latest_installer_version != current_version:
                self.update_installer_label['text'] = f"Please update installer, new version {latest_installer_version} available!"
                self.update_installer_label['background'] = "#e85d04"
                self.update_installer_label.pack(side="top", fill=tkinter.X, after=self.response_status)
                browser_update_button = ttk.Button(self, text="New Installer version", style='W6.TButton', command=request.open_installer_release_page_browser)
                CreateToolTip(browser_update_button, f"New available version {latest_installer_version}, click to open webpage!")
                browser_update_button.pack(side="top", before=self.browse_button)
        except KeyError:
            pass

    def check_if_update_available(self):
        self.filler_label['text'] = ""
        self.filler_label['background'] = "#1B1B1B"
        try:
            manifest_path = Path(f'{self.destination_folder}\\A32NX\\manifest.json')
            if manifest_path.is_file():
                manifest_file = open(manifest_path, 'r')
                manifest_version = json.load(manifest_file)["package_version"]
                self.latest_release_version = json.load(request.get(latest_release_url))['tag_name'].strip("v")
                CreateToolTip(self.download_stable_btn, f"Latest available stable version: {self.latest_release_version}")
                if manifest_version == self.latest_release_version and self.get_asset_json_path().is_file():
                    with open(self.get_asset_json_path()) as file:
                        asset_data = json.load(file)
                        if 'stable' in asset_data.keys():
                            self.filler_label['text'] = f"Stable version {self.latest_release_version} is up to date!"
                            self.filler_label['background'] = "green"
                            return
                        local_asset_id = asset_data['id']
                        self.development_github_data = json.load(request.get(master_prerelease_url))
                        latest_master_asset_id = self.development_github_data['assets'][0]['id']
                        self.latest_development_update_timestamp = self.development_github_data['assets'][0]['updated_at']
                        if local_asset_id == latest_master_asset_id:
                            self.filler_label['text'] = "Development version is up to date!"
                            self.filler_label['background'] = "green"
                        else:
                            self.filler_label['text'] = "Development version is out of date, please consider updating!"
                            self.filler_label['background'] = "#e85d04"
                        date, timestamp = self.latest_development_update_timestamp.split('T')
                        CreateToolTip(self.download_dev_btn, f"Latest development version was updated at: {date} {timestamp}")
                else:
                    self.filler_label['text'] = "A32NX version is out of date, please consider updating!"
                    self.filler_label['background'] = "#e85d04"
        except KeyError:
            self.filler_label['text'] = "Could not check for updates! Github API rate limit could be exceeded."
            self.filler_label['background'] = "#e85d04"
        except AttributeError:
            self.filler_label['text'] = "Could not check for updates, first time installing?."
            self.filler_label['background'] = "#e85d04"
        self.filler_label.update()

    @staticmethod
    def convert_from(windows_timestamp: int) -> str:
        unix_epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
        windows_epoch = datetime(1601, 1, 1, tzinfo=timezone.utc)
        epoch_delta = unix_epoch - windows_epoch
        windows_timestamp_in_seconds = windows_timestamp / 10_000_000
        unix_timestamp = windows_timestamp_in_seconds - epoch_delta.total_seconds()
        return datetime.utcfromtimestamp(unix_timestamp).strftime('%Y-%m-%dT%H:%M:%SZ')

    def unzip_file(self, file_name: str, stable: bool):
        self.response_status['text'] = "Installing A32NX..."
        self.response_status['background'] = ""
        self.response_status.update()
        try:
            archive = zipfile.ZipFile(f'{sys.prefix}/{file_name}')
            for file in archive.namelist():
                if not os.path.isdir(file):
                    archive.extract(file, path=self.destination_folder)
            archive.close()
            if stable:
                latest_master_asset = {'stable': 'True'}
            else:
                try:
                    latest_master_asset = self.development_github_data['assets'][0]
                except KeyError:
                    self.development_github_data = json.load(request.get(master_prerelease_url))
                    latest_master_asset = self.development_github_data['assets'][0]
            with open(self.get_asset_json_path(), 'w', encoding='utf-8') as f:
                # Store asset.json in A32NX folder so we can check it later to see if it is still up to date
                json.dump(latest_master_asset, f, ensure_ascii=False, indent=4)
            self.response_status['background'] = "green"
            self.response_status['text'] = "A32NX correctly installed, everything should be good to go!"
        except (zipfile.BadZipfile, OSError, AttributeError) as e:
            self.response_status['background'] = "firebrick"
            self.response_status['text'] = f"Folder could not be unzipped, failed reason: {repr(e)}!"
        except (json.decoder.JSONDecodeError, KeyError):
            self.response_status['background'] = "#e85d04"
            self.response_status['text'] = f"A32NX correctly installed but could not write version status, Github was not reachable!"
        self.exit.pack(side="bottom", pady=(20, 0), padx=(184, 184))
        self.cancel.pack_forget()
        self.pack()

    def on_enter(self, e):
        self.original_background = ttk.Style().configure(e.widget['style'])['background']
        ttk.Style().configure(e.widget['style'], background="#506164")

    def on_leave(self, e):
        ttk.Style().configure(e.widget['style'], background=self.original_background)

def set_download_server(menu, value):
    global active_download_server
    active_download_server = value
    menu.entryconfigure(2, label=f"Active Download server: {active_download_server}")

if __name__ == '__main__':
    request = Request()
    root = tkinter.Tk()
    root.resizable(False, False)
    root.iconbitmap(default=f"{sys.prefix}/icon.ico")
    style = ttk.Style(root)
    style.theme_use("clam")
    style.configure(root, background='#1B1B1B')
    style.configure(root, foreground='white')
    menubar = tkinter.Menu(root)
    download_server = tkinter.Menu(menubar, tearoff=False)
    download_server.add_command(label="- Github", command=lambda server='Github': set_download_server(menubar, server))
    download_server.add_command(label="- FlyByWire CDN", command=lambda server='FlyByWire CDN': set_download_server(menubar, server))
    download_server.add_command(label="- FlyByWire non CDN", command=lambda server='FlyByWire non CDN': set_download_server(menubar, server))
    menubar.add_cascade(label="Change Download server", menu=download_server)
    menubar.add_cascade(label=f"Active Download server: {active_download_server}")
    root.config(menu=menubar)
    ttk.Style().configure('W1.TButton', background="#6399AE")
    ttk.Style().configure('W2.TButton', background="#00C2CB")
    ttk.Style().configure('W3.TButton', background="#545454")
    ttk.Style().configure('W4.TButton', background="#545454")
    ttk.Style().configure('W5.TButton', background="#545454")
    ttk.Style().configure('W6.TButton', background="#00C2CB")
    ttk.Style().configure('D1.TMenubutton', background="#545454", foreground="white", width=70, borderwidth=0)
    style.layout('text.Horizontal.TProgressbar',
                 [('Horizontal.Progressbar.trough',
                   {'children': [('Horizontal.Progressbar.pbar',
                                  {'side': 'left', 'sticky': 'ns'})],
                    'sticky': 'nswe'}),
                  ('Horizontal.Progressbar.label', {'sticky': ''})])
    style.configure('text.Horizontal.TProgressbar', text='0 %')
    style.configure('text.Horizontal.TProgressbar', background='green')
    app = Application(master=root)
    app.mainloop()


"""
Purpose: provides an example of using sklearn_gbm_wrapper module
using California housing dataset in which on numerical feature (Population)
is converted to categorical feature

Data created: 2018-06-28

Author: Alex Radnaev

"""

import sklearn.datasets as sklearn_ds
import sklearn_gbm_ots as gbm_ots
import pandas as pd

def population_mapper(x):
    if x < 1000:
        return '<1k'
    elif x < 2000:
        return '1k-2k'
    else:
        return '2k+'

def prepare_california_housing_dataframe(outcome):
    """Returns california housing dataset as a dataframe

    arguments:
        outcome - column name for the outcome"""
    housing_dataset = sklearn_ds.california_housing.fetch_california_housing()
    X = housing_dataset.data
    y = housing_dataset.target

    df_X = pd.DataFrame(X, columns=housing_dataset.feature_names)
    df_y = pd.DataFrame(y, columns=[outcome])
    df = pd.concat([df_X, df_y], axis=1)
    df['Population'] = df['Population'].apply(population_mapper)

    return df

def run_gbm():
    """Prepares California housing dataset and build gbm model

    Returns:
        fitted sklearn_gbm_wrapper.GBMwrapper object"""
    outcome = 'house price, 100k'

    df = prepare_california_housing_dataframe(outcome)

    housing_gbm = gbm_ots.GBMwrapper(
        df[:100],
        outcome,
        show_plots=False,
        random_state=2018)

    gbm_params = {
        'n_estimators': 300,
        'max_depth': 3,
        'max_features': 5,
        'min_samples_split': 10,
        'min_samples_leaf': 5,
        'learning_rate': 0.5,
        'loss': 'ls'}

    housing_gbm.build_model(params=gbm_params)

    return housing_gbm

def custom_plotting(gbm):
    """Demonstrates how to customize plots"""
    gbm.plot_output(
        ax_limits={'AveBedrms': {'xlim': [0.9, 1.2]}},
        figsize=(11, 11),
        absolute_yticks=False,
        absolute_yscale=False,
        file_prefix='relative_scale_relative_ticks')

    gbm.plot_output(
        ax_limits={'AveBedrms': {'xlim': [0.9, 1.2]}},
        figsize=(11, 11),
        absolute_yticks=True,
        absolute_yscale=False,
        file_prefix='relative_scale_abs_ticks')

    # gbm.plot_output(
    #     ax_limits={'AveBedrms': {'xlim': [0.9, 1.2]}},
    #     figsize=(11, 11),
    #     absolute_yticks=False,
    #     absolute_yscale=True,
    #     file_prefix='abs_scale_relative_ticks')

    # gbm.plot_output(
    #     ax_limits={'AveBedrms': {'xlim': [0.9, 1.2]}},
    #     figsize=(11, 11),
    #     absolute_yticks=True,
    #     absolute_yscale=True,
    #     file_prefix='abs_scale_abs_ticks')

def main():
    housing_gbm = run_gbm()
    custom_plotting(housing_gbm)

if __name__ == '__main__':
    main()


from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from drf_yasg.utils import swagger_auto_schema
from rest_framework import permissions
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from hotelcontent.models import Hotel, Rooms, Bookings, AgentReservation, User
from .functions import str_to_date, final_price_list, final_price
from .serializers import (
    HotelsSerializer,
    BookingSerializer,
    FilterHotelsSerializer,
    CancelBookingSerializer,
    CreateBookingSerializer,
    RateSerializer,
    RateFilterSerializer,
)

class HotelsView(APIView):
    """Endpoint for viewing available hotels"""

    @swagger_auto_schema(responses={200: HotelsSerializer})
    def get(self, request):
        hotels = Hotel.objects.all()
        serializer = HotelsSerializer(hotels, many=True)
        return Response({"hotels": serializer.data})

    @swagger_auto_schema(
        request_body=FilterHotelsSerializer, responses={200: HotelsSerializer}
    )
    def post(self, request):
        """Filters hotels by coordinates within a certain radius"""

        filter_hotels = []

        lat = request.data.get("latitude")
        long = request.data.get("longitude")
        rad = request.data.get("radius")

        for p in Hotel.objects.raw(
            "SELECT * FROM hotelcontent_hotel WHERE earth_distance(ll_to_earth(%s,%s),ll_to_earth(float8("
            "hotel_lat), float8(hotel_long))) / 1000 <= %s", [lat, long, rad]
        ):
            filter_hotels.append(p)

        serializer = HotelsSerializer(filter_hotels, many=True)

        return Response({"hotels in range " + str(rad) + "km": serializer.data})

class CancelBooking(APIView):
    """Endpoint for canceling active bookings"""

    permission_classes = [permissions.IsAuthenticated]

    @swagger_auto_schema(
        request_body=CancelBookingSerializer,
        responses={200: "This booking has been canceled"},
    )
    def post(self, request):
        """Cancel booking by id"""
        user_name = request.user.username
        user = User.objects.get(username=user_name)
        agent = AgentReservation.objects.get(agent=user)
        booking_id = request.data.get("booking_id")
        try:
            booking = Bookings.objects.get(id=booking_id, agent_reservation=agent)
            booking.booking_stat = False
            booking.save()
            return Response("This booking has been canceled", status=status.HTTP_200_OK)
        except ObjectDoesNotExist:
            return Response(
                "Error! Booking with this id doesn't exist!",
                status=status.HTTP_404_NOT_FOUND,
            )

class BookingView(APIView):
    """Endpoint for booking a rate"""

    permission_classes = [permissions.IsAuthenticated]

    @swagger_auto_schema(
        request_body=CreateBookingSerializer, responses={201: "Successfully booked"}
    )
    def post(self, request):
        """Book the rate by checkin, checkout date, room number and hotel"""
        agent_name = request.user.username
        agent_user = User.objects.get(username=agent_name)
        agent = AgentReservation.objects.get(agent=agent_user)

        start_date, end_date = str_to_date(request)

        if start_date == "error":
            return Response(
                "start_date and end_date must be not Empty",
                status=status.HTTP_400_BAD_REQUEST,
            )
        if start_date == "incorrect date":
            return Response(
                "Incorrect start_date or end_date", status=status.HTTP_400_BAD_REQUEST
            )

        room_num = int(request.data.get("room_number"))
        hotel = Hotel.objects.get(hotel_name=request.data.get("hotel"))
        room = Rooms.objects.get(room_number=room_num, hotel=hotel)

        if Rooms.objects.filter(
            Q(id=room.id)
            & Q(bookings__booking_stat=True)
            & (
                (
                    Q(bookings__checkin__lt=end_date)
                    & Q(bookings__checkout__gte=end_date)
                )
                | (
                    Q(bookings__checkin__lte=start_date)
                    & Q(bookings__checkout__gt=start_date)
                )
            )
        ).exists():
            return Response(
                "Not created, Booking is exist", status=status.HTTP_400_BAD_REQUEST
            )

        room = final_price(
            room=room, start_date=start_date, end_date=end_date, request=request
        )

        booking = Bookings(
            agent_reservation=agent,
            booking_stat=True,
            hotels=hotel,
            checkin=start_date,
            checkout=end_date,
            rate_price=room.room_price,
            room=room,
            room_number=room.room_number,
            hotel=hotel.hotel_name,
        )
        booking.save()
        return Response("Successfully created", status=status.HTTP_201_CREATED)

class RatesFilterDateView(APIView):
    """Endpoint for viewing available rates"""

    @swagger_auto_schema(
        request_body=RateFilterSerializer, responses={200: RateSerializer}
    )
    def post(self, request):
        """Filter available rates by checkin and checkout date"""
        start_date, end_date = str_to_date(request)

        free_rooms = list(
            Rooms.objects.exclude(
                Q(bookings__booking_stat=True)
                & (
                    (
                        Q(bookings__checkin__lt=end_date)
                        & Q(bookings__checkout__gte=end_date)
                    )
                    | (
                        Q(bookings__checkin__lte=start_date)
                        & Q(bookings__checkout__gt=start_date)
                    )
                )
            )
        )
        free_rooms = final_price_list(
            free_rooms=free_rooms,
            start_date=start_date,
            end_date=end_date,
            request=request,
        )
        serializer = RateSerializer(free_rooms, many=True)
        return Response({"rooms": serializer.data})

class MyBookingsView(APIView):
    """Endpoint for viewing available bookings"""

    permission_classes = [permissions.IsAuthenticated]

    @swagger_auto_schema(responses={200: BookingSerializer})
    def get(self, request):
        agent_name = request.user.username
        agent_object = User.objects.get(username=agent_name)
        agent = AgentReservation.objects.get(agent=agent_object)

        bookings = Bookings.objects.filter(agent_reservation=agent)
        serializer = BookingSerializer(bookings, many=True)
        return Response({"Bookings": serializer.data})


import os
import random
import shutil

import pytest
from helpers import copy_sample_files, random_string
from iiif_pipeline.derivatives import create_jp2
from iiif_pipeline.helpers import matching_files

FIXTURES_FILEPATH = os.path.join("fixtures", "tif")
SOURCES = [os.path.join("/", "source"),
           os.path.join("/", "source folder")]
DERIVATIVES = [os.path.join("/", "derivatives"),
               os.path.join("/", "derivatives folder")]
UUIDS = [random_string() for x in range(random.randint(2, 3))]
PAGE_COUNT = random.randint(1, 5)

def setup():
    """Sets up source directory for tests."""
    for SOURCE_DIR, DERIVATIVE_DIR in zip(SOURCES, DERIVATIVES):
        for d in [SOURCE_DIR, DERIVATIVE_DIR]:
            if os.path.isdir(d):
                shutil.rmtree(d)
        shutil.copytree(FIXTURES_FILEPATH, SOURCE_DIR)
        copy_sample_files(SOURCE_DIR, UUIDS, PAGE_COUNT, "tif")
        os.makedirs(DERIVATIVE_DIR)

def test_create_jp2():
    """Ensure the run method produces the expected number of files."""
    for SOURCE_DIR, DERIVATIVE_DIR in zip(SOURCES, DERIVATIVES):
        uuid = random.choice(UUIDS)
        tiff_files = matching_files(
            SOURCE_DIR,
            prefix=uuid,
            skip=False,
            prepend=True)
        create_jp2(tiff_files, uuid, DERIVATIVE_DIR)
        assert len(os.listdir(DERIVATIVE_DIR)) == PAGE_COUNT
        for f in os.listdir(DERIVATIVE_DIR):
            assert ".tif" not in f

def test_replace_jp2():
    """Ensure replacing of files is handled correctly.

    If files exist and the replace parameter is False, a FileExistsError should
    be raised.
    """
    for SOURCE_DIR, DERIVATIVE_DIR in zip(SOURCES, DERIVATIVES):
        uuid = random.choice(UUIDS)
        tiff_files = matching_files(
            SOURCE_DIR,
            prefix=uuid,
            skip=False,
            prepend=True)
        create_jp2(tiff_files, uuid, DERIVATIVE_DIR)
        with pytest.raises(FileExistsError):
            create_jp2(tiff_files, uuid, DERIVATIVE_DIR)
        create_jp2(tiff_files, uuid, DERIVATIVE_DIR, replace=True)

def teardown():
    """Removes test directories."""
    for SOURCE_DIR, DERIVATIVE_DIR in zip(SOURCES, DERIVATIVES):
        for d in [SOURCE_DIR, DERIVATIVE_DIR]:
            shutil.rmtree(d)


import argparse, sys, time, json
sys.path.append('/home/zhhao/audioST/instruct_speech_llama')
from transformers import AutoTokenizer, AutoModelForCausalLM
from tqdm import tqdm
import torch, transformers
from instruct_speech_new.eval.utils import disable_torch_init
from instruct_speech_new.model.model import SpeechLlamaForCausalLM, SpeechLlamaConfig
from instruct_speech_new.model.utils import KeywordsStoppingCriteria
from fairseq.data.audio.speech_to_text_dataset import _collate_frames
from instruct_speech_new.train.dataset import PromptSpeechToTextDatasetCreator, SpeechToTextDatasetItem
from instruct_speech_new import conversation as conversation_lib
from instruct_speech_new.conversation import SeparatorStyle

import os
import requests

import torch.nn.functional as F

IGNORE_INDEX = -100
DEFAULT_PAD_TOKEN = "[PAD]"
DEFAULT_EOS_TOKEN = "</s>"
DEFAULT_BOS_TOKEN = "<s>"
DEFAULT_UNK_TOKEN = "<unk>"
DEFAULT_SPEECH_TOKEN = "<speech>"
DEFAULT_SPEECH_PATCH_TOKEN = "<sp_patch>"
DEFAULT_SPEECH_START_TOKEN = "<sp_start>"
DEFAULT_SPEECH_END_TOKEN = "<sp_end>"

prompt_list = ['<speech_here> Start by converting the English audio into German written form.',]

def eval_model(args):
    load_type = torch.float16 # torch.float32
    # Model
    disable_torch_init()
    model_name = os.path.expanduser(args.model_name)
    tokenizer = transformers.AutoTokenizer.from_pretrained(
        args.model_name,
        padding_side="right",
        use_fast=False,
    )
    config = json.load(open(os.path.join(args.model_name, 'config.json')))
    config['large_model'] = True
    update_config = os.path.join(args.model_name, 'config_large.json')
    json.dump(config, open(update_config, 'w'), indent=2)
    model = SpeechLlamaForCausalLM.from_pretrained(args.model_name,
                                                   torch_dtype=load_type,
                                                   low_cpu_mem_usage=True,
                                                   device_map='auto',
                                                   config=update_config,).eval()
    if 'model.embed_tokens' in model.hf_device_map.keys():
        device_input = 'cuda:' + str(model.hf_device_map['model.embed_tokens'])
        device_output = 'cuda:' + str(model.hf_device_map['lm_head'])
    else:
        device_input = 'cuda'
        device_input = 'cuda'
    length_after_ssl, length_after_adp = model.model.initialize_speech_modules(
        speech_tower_path=model.config.speech_tower_path,
        speech_tower_type=None,
        len_adapter_channels=model.config.len_adapter_channels,
        len_adapter_kernel_sizes=model.config.len_adapter_kernel_sizes,
        ssl_fintuned=model.config.ssl_fintuned,
    )
    model.model.speech_tower.to(dtype=load_type, device=device_input)

    length_adapter_weights = torch.load(args.length_adapter_path, map_location='cpu')
    mlp_adapter_weights = torch.load(args.mlp_adapter_path, map_location='cpu')

    model.model.mm_length_adapter.load_state_dict(length_adapter_weights)
    model.model.mm_mlp_adapter.load_state_dict(mlp_adapter_weights)

    model.model.mm_length_adapter.to(dtype=load_type, device=device_input)
    model.model.mm_mlp_adapter.to(dtype=load_type, device=device_input)

    test_dataset = PromptSpeechToTextDatasetCreator.from_tsv(args.data_path, args.data_split)

    if not os.path.exists(os.path.join(args.result, args.data_split)):
        os.makedirs(os.path.join(args.result, args.data_split))

    ref_file = open(os.path.join(args.result, args.data_split, "ref"), "w")
    hyp_file = open(os.path.join(args.result, args.data_split, "hyp"), "w")
    conv = conversation_lib.default_conversation.copy()
    for test_data in tqdm(test_dataset):
        source, ref, id = test_data.source, test_data.target, test_data.id
        speech_batch = _collate_frames([source], is_audio_input=True)
        n_frames = torch.tensor([source.size(0)], dtype=torch.long)
        speech_lens = length_after_adp(length_after_ssl(n_frames))

        to_adds = [int(speech_len)*DEFAULT_SPEECH_PATCH_TOKEN for speech_len in speech_lens]
        to_adds = [DEFAULT_SPEECH_START_TOKEN + to_add + DEFAULT_SPEECH_END_TOKEN for to_add in to_adds]

        qs = prompt_list[0]
        before, after = qs.split('<speech_here>')
        mm_prompts = [before + to_add + after for to_add in to_adds]

        conv.messages = []
        conv.append_message(conv.roles[0], mm_prompts[0])
        conv.append_message(conv.roles[1], None)
        prompt_inputs = conv.get_prompt()
        inputs = tokenizer([prompt_inputs])
        input_ids = torch.as_tensor(inputs.input_ids).cuda()

        stop_str = conv.sep if conv.sep_style != SeparatorStyle.TWO else conv.sep2
        keywords = [stop_str]
        stopping_criteria = KeywordsStoppingCriteria(keywords, tokenizer, input_ids)
        with torch.inference_mode():
            output_ids = model.generate(
                attention_mask=input_ids.ne(tokenizer.pad_token_id),
                input_ids=input_ids.to(device=device_input),
                speech_batch=speech_batch.to(dtype=load_type, device=device_input),
                src_lengths=n_frames.to(device=device_input),
                after_lens=speech_lens.to(device=device_input),
                #do_sample=True,
                #temperature=0.8,
                num_beams=args.beam,
                max_new_tokens=500,
                stopping_criteria=[stopping_criteria])
        input_token_len = input_ids.shape[1]
        outputs = tokenizer.batch_decode(output_ids[:, input_token_len:], skip_special_tokens=True)[0]
        outputs = outputs.strip()
        print(outputs)
        if outputs.endswith(stop_str):
            outputs = outputs[:-len(stop_str)]
        outputs = outputs.strip()
        print(f"{id} decode complete,\nref:{ref} \nhyp:{outputs}")
        print(f"{id}\t{ref}", file=ref_file)
        print(f"{id}\t{outputs}", file=hyp_file)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--model-name", type=str, default="facebook/opt-350m")
    parser.add_argument("--length-adapter-path", type=str, required=True)
    parser.add_argument("--mlp-adapter-path", type=str, required=True)
    parser.add_argument("--data-path", type=str, required=True)
    parser.add_argument("--data-split", type=str, required=True)
    parser.add_argument("--result", type=str, required=True)
    parser.add_argument("--beam", type=int, default=1)
    args = parser.parse_args()

    eval_model(args)


import requests

# SpotifyClient is the class that interacts with the Spotify API endpoint to retrieve the
# data we need to make the necessary predictions
class SpotifyClient(object):
    def __init__(self, api_key, song_id, features):
        '''Initializing the class variables for the instance
        '''
        self.api_key = api_key
        self.song_id = song_id
        self.features = features

    def get_track_features(self):
        '''Method that will retrieve the audio features of the track and retuen it in a list.
        '''

        # Formatting the URL of the API endpoint we want to interact with
        url = f'https://api.spotify.com/v1/audio-features/{self.song_id}'

        # Making a request to the URL and recieving the response
        response = requests.get(
            url,
            headers={
                "Content-Type": "application/json",
                "Authorization": f'Bearer {self.api_key}'
            }
        )

        # Converting the response into a python dict
        response_json = response.json()

        value_list = []

        # Adding each of the features to the list
        for feature in self.features:
            try:
                value_list.append(response_json[feature])
            except:
                # Handling error where the access token is expired
                if 'error' in response_json.keys():
                    return None
                value_list.append(None)

        return value_list

    def get_track_analysis(self):
        '''Method that will retrieve the audio analysis and extract the necessary data
        '''

        # Formatting the URL of the API endpoint we want to interact with
        url = f'https://api.spotify.com/v1/audio-analysis/{self.song_id}'

        # Making a request to the URL and recieving the response
        response = requests.get(
            url,
            headers={
                "Content-Type": "application/json",
                "Authorization": f'Bearer {self.api_key}'
            }
        )

        # Converting the response into a python dict
        response_json = response.json()

        try:
            sections = response_json['sections']
            section = sections[2]
            chorus_hit = section['start']
        except:
            # Handling error where the access token is expired
            if 'error' in response_json.keys():
                return None
            sections = []
            chorus_hit = None

        return [chorus_hit, len(sections)]

    def get_track_artists(self):
        '''Method that will retrieve the artist of the track
        '''

        # Formatting the URL of the API endpoint that we want to interact with
        url = f'https://api.spotify.com/v1/tracks/{self.song_id}'

        # Making a request to the endpoint and recieving a response
        response = requests.get(
            url,
            headers={
                "Content-Type": "application/json",
                "Authorization": f'Bearer {self.api_key}'
            }
        )

        # Converting the response to a python dict
        response_json = response.json()

        try:
            artists = response_json['artists']
            artist = artists[0]
            artist_name = artist['name']
        except:
            # Handling error where the access token has expired
            if 'error' in response_json.keys():
                return None
            artist_name = None

        return [artist_name]


import sys

from pyspark.sql import DataFrame
from pyspark.sql.types import StructType, StructField, StringType, IntegerType

from pysparkexamples import spark, PROJECT_ROOT

JSON_FILE = f"{PROJECT_ROOT}/data/emp_details.json"
URI_SCHEME = "file://"
FILE_SCHEMA = StructType([
    StructField("userId", StringType(), True),
    StructField("jobTitle", StringType(), True),
    StructField("firstName", StringType(), True),
    StructField("lastName", StringType(), True),
    StructField("employeeCode", StringType(), True),
    StructField("region", StringType(), True),
    StructField("phoneNumber", StringType(), True),
    StructField("emailAddress", StringType(), True),
    StructField("salary", IntegerType(), True)
])

def read_json(uri_scheme: str = URI_SCHEME, file_name: str = JSON_FILE) -> DataFrame:
    """
    Function used to read json file
    it will read data/emp_details.json by default

    :param uri_scheme: uri scheme e.g. file, hdfs, gcs, s3
    :param file_name: absolute path of file to read
    :return: DataFrame object
    """
    # NOTE - for reading file from local system use UNIX like path string
    print(f"Reading JSON File: {JSON_FILE}")
    input_path = f"{uri_scheme}/{file_name}" if sys.platform == "win32" else f"{uri_scheme}{file_name}"
    return spark.read.json(input_path)

def write_json(df: DataFrame, output_dir: str) -> None:
    """
    Function used to write a DataFrame to json file

    :param df: Dataframe object
    :param output_dir: relative or absolute path of target file
    :return: None
    """
    print(f"Writing DataFrame to JSON file {output_dir}")
    df.write.json(output_dir, mode="overwrite")


# IMPORTING LIBS AND MODEL
import streamlit as st
import joblib

model = joblib.load('titanic_model_svm.sav')

# DATA INPUT
st.image('imgs/titanic.jpg')
st.title("Titanic - Prediction of survival")
name = st.text_input('Name')
last_name = st.text_input('Last Name')
title = st.selectbox('Title', ('Mrs', 'Miss', 'Master', 'Mr', 'Other'))
sex = st.radio("Sex", ('Male', 'Female'))
age = st.slider('Age', 0.0, 130.0, 25.0)
embarked = st.selectbox('Embarked in', ('Cherbourg', 'Queenstown', 'Southampton'))
psg_class = st.selectbox('Class', (1,2,3))
cabin = st.text_input('Cabin')
ticket = st.text_input('Ticket')
fare = st.slider('Fare', 0.0, 513.0, 15.0)
sibsp = st.number_input("Number of siblings/spouses aboard the Titanic", value=0, min_value=0, step=1)
parch = st.number_input("Number of parents/children aboard the Titanic", value=0, min_value=0, step=1)

# DATA TREATMENT
sex = 1 if sex == "Male" else 0
embarked = 0 if embarked == "Cherbourg" else 2 if embarked == "Southampton" else 1
isAlone = 1 if (sibsp + parch) == 0 else 0

if title == "Mr":
    title = 2
elif title == "Mrs":
    title = 3
elif title == "Miss":
    title = 1
elif title == "Master":
    title = 0
else:
    title = 4

if fare > 31.0:
    fare = 3
elif fare > 14.454:
    fare = 2
elif fare > 7.91:
    fare = 1
else:
    fare = 0

if age > 64.0:
    age = 4
elif age > 48.0:
    age = 3
elif age > 32.0:
    age = 2
elif age > 16.0:
    age = 1
else:
    age = 0

# PREDICTION
btn_predict = st.button("Predict")
if btn_predict:
    result = model.predict([[psg_class,sex,embarked,age,fare,title,isAlone]])
    st.subheader("The passenger is... ")
    result = "Survived" if result == 1 else "Died"
    st.write(result)


from dataclasses import dataclass
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ib_tws_server.ib_imports import *
import inspect
import logging
from typing import Callable, Dict, FrozenSet, List, MutableSet, Set, Type

logger = logging.getLogger(__name__)

class ApiDefinition:
    request_method: Callable = None
    cancel_method: Callable = None
    callback_methods: List[Callable] = None
    done_method: Callable = None
    uses_req_id: bool = False
    has_done_flag: bool = False
    is_subscription: bool = False
    subscription_flag_name: str = None
    subscription_flag_value: bool = True

    def __init__(self, request_method: Callable,
        cancel_method: Callable = None,
        callback_methods: List[Callable] = None,
        done_method: Callable = None,
        uses_req_id: bool = False,
        has_done_flag: bool = False,
        is_subscription: bool = False,
        subscription_flag_name: str = None,
        subscription_flag_value: bool = True):
        self.request_method = request_method
        self.cancel_method = cancel_method
        self.callback_methods = callback_methods
        self.done_method = done_method
        self.uses_req_id = uses_req_id
        self.has_done_flag = has_done_flag
        self.is_subscription = is_subscription
        self.subscription_flag_name = subscription_flag_name
        self.subscription_flag_value = subscription_flag_value

        if self.is_subscription or self.subscription_flag_name is not None:
            if not self.callback_methods or len(self.callback_methods) == 0:
                raise RuntimeError(f"Subscriptions should always have one or more callbacks {request_method.__name__}")

        if self.subscription_flag_name is not None:
            self.is_subscription = False

    @staticmethod
    def verify():
        ignore_methods = IGNORE_METHODS.copy()

        def log_unprocessed_methods():
            predicate = lambda x: inspect.isfunction(x) and not inspect.isbuiltin(x) and not inspect.ismethoddescriptor(x) and not x.__name__.startswith("__")
            for name,val in inspect.getmembers(EClient, predicate):
                if not val in ignore_methods:
                    logger.error(f"Unhandled request: {name}")
            for name,type in inspect.getmembers(EWrapper, predicate):
                if not val in ignore_methods:
                    logger.error(f"Unhandled response: {name}")

        def flag_request_definition(definition: ApiDefinition):
            if (definition.request_method is not None):
                ignore_methods.add(definition.request_method)
            if (definition.cancel_method is not None):
                ignore_methods.add(definition.cancel_method)
            if (definition.callback_methods is not None):
                for m in definition.callback_methods:
                    ignore_methods.add(m)
            if (definition.done_method is not None):
                ignore_methods.add(definition.done_method)

        for d in REQUEST_DEFINITIONS: flag_request_definition(d)
        log_unprocessed_methods()

class TypeDefinition:
    container_type: type
    item_type: str

    def __init__(self, container_type: type, item_type: str):
        self.container_type = container_type
        self.item_type = item_type

IGNORE_METHODS: FrozenSet[Callable] = set({
    EClient.connect,
    EWrapper.connectAck,
    EWrapper.connectionClosed,
    EClient.disconnect,
    EWrapper.error,
    EClient.isConnected,
    EClient.keyboardInterrupt,
    EClient.keyboardInterruptHard,
    EWrapper.logAnswer,
    EClient.logRequest,
    # Enable when using 9.85.x
    #EClient.msgLoopRec,
    #EClient.msgLoopTmo,
    EClient.reset,
    EClient.run,
    EClient.sendMsg,
    EClient.serverVersion,
    EClient.setConnState,
    # Enable when using 9.85.x
    #EClient.setConnectionOptions,
    EClient.startApi,
    EClient.twsConnectionTime,
    EWrapper.verifyAndAuthCompleted,
    EClient.verifyAndAuthMessage,
    EWrapper.verifyAndAuthMessageAPI,
    EClient.verifyAndAuthRequest,
    EWrapper.verifyCompleted,
    EClient.verifyMessage,
    EWrapper.verifyMessageAPI,
    EClient.verifyRequest,
    EWrapper.winError
})

# Mappings of requests with their callbacks.
REQUEST_DEFINITIONS: List[ApiDefinition] = [
    ApiDefinition(request_method=EClient.queryDisplayGroups,
        callback_methods=[EWrapper.displayGroupList],
        uses_req_id=True),
    ApiDefinition(request_method=EClient.exerciseOptions, uses_req_id=True),
    ApiDefinition(request_method=EClient.calculateImpliedVolatility,
        callback_methods=[EWrapper.tickOptionComputation],
        cancel_method=EClient.cancelCalculateImpliedVolatility,
        is_subscription = True,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.calculateOptionPrice,
        callback_methods=[EWrapper.tickOptionComputation],
        cancel_method=EClient.cancelCalculateOptionPrice,
        is_subscription = True,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqAccountSummary,
        callback_methods=[EWrapper.accountSummary],
        cancel_method=EClient.cancelAccountSummary,
        done_method=EWrapper.accountSummaryEnd,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqAccountUpdatesMulti,
        callback_methods=[EWrapper.accountUpdateMulti],
        cancel_method=EClient.cancelAccountUpdatesMulti,
        done_method=EWrapper.accountUpdateMultiEnd,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqFundamentalData,
        callback_methods=[EWrapper.fundamentalData],
        cancel_method=EClient.cancelFundamentalData,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqHeadTimeStamp,
        callback_methods=[EWrapper.headTimestamp],
        cancel_method=EClient.cancelHeadTimeStamp,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqHistogramData,
        callback_methods=[EWrapper.histogramData],
        cancel_method=EClient.cancelHistogramData,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqHistoricalData,
        callback_methods=[EWrapper.historicalData, EWrapper.historicalDataUpdate],
        cancel_method=EClient.cancelHistoricalData,
        done_method=EWrapper.historicalDataEnd,
        subscription_flag_name = 'keepUpToDate',
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqMktData,
        callback_methods=[EWrapper.tickPrice, EWrapper.tickSize,
            EWrapper.tickEFP,
            EWrapper.tickNews, EWrapper.rerouteMktDataReq,
            EWrapper.rerouteMktDepthReq, EWrapper.tickReqParams,
            EWrapper.deltaNeutralValidation],
        cancel_method=EClient.cancelMktData,
        done_method=EWrapper.tickSnapshotEnd,
        subscription_flag_name = 'snapshot',
        subscription_flag_value = False,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqMktDepth,
        callback_methods=[EWrapper.updateMktDepth, EWrapper.updateMktDepthL2],
        cancel_method=EClient.cancelMktDepth,
        is_subscription = True,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqNewsBulletins,
        callback_methods=[EWrapper.updateNewsBulletin],
        cancel_method=EClient.cancelNewsBulletins,
        is_subscription = True),
    ApiDefinition(request_method=EClient.placeOrder,
        callback_methods=[EWrapper.orderStatus],
        is_subscription = True,
        cancel_method=EClient.cancelOrder),
    ApiDefinition(request_method=EClient.reqPnL,
        callback_methods=[EWrapper.pnl],
        cancel_method=EClient.cancelPnL,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqPnLSingle,
        callback_methods=[EWrapper.pnlSingle],
        cancel_method=EClient.cancelPnLSingle,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqPositions,
        callback_methods=[EWrapper.position],
        done_method=EWrapper.positionEnd,
        cancel_method=EClient.cancelPositions),
    ApiDefinition(request_method=EClient.reqPositionsMulti,
        callback_methods=[EWrapper.positionMulti],
        done_method=EWrapper.positionMultiEnd,
        cancel_method=EClient.cancelPositionsMulti,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqRealTimeBars,
        callback_methods=[EWrapper.realtimeBar],
        cancel_method=EClient.cancelRealTimeBars,
        is_subscription = True,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqScannerSubscription,
        callback_methods=[EWrapper.scannerData],
        done_method=EWrapper.scannerDataEnd,
        cancel_method=EClient.cancelScannerSubscription,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqTickByTickData,
        callback_methods=[EWrapper.tickByTickAllLast, EWrapper.tickByTickBidAsk, EWrapper.tickByTickMidPoint],
        cancel_method=EClient.cancelTickByTickData,
        is_subscription = True,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.replaceFA,
        # Enable when using 9.85.x
        #done_method=EWrapper.replaceFAEnd,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqAccountUpdates,
        callback_methods=[EWrapper.updateAccountValue, EWrapper.updateAccountTime, EWrapper.updatePortfolio],
        done_method=EWrapper.accountDownloadEnd),
    ApiDefinition(request_method=EClient.reqOpenOrders),
    ApiDefinition(request_method=EClient.reqAllOpenOrders),
    ApiDefinition(request_method=EClient.reqAutoOpenOrders),
    ApiDefinition(request_method=EClient.reqContractDetails,
        callback_methods=[EWrapper.contractDetails,EWrapper.bondContractDetails],
        done_method=EWrapper.contractDetailsEnd,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqCurrentTime,
        callback_methods=[EWrapper.currentTime]),
    ApiDefinition(request_method=EClient.reqCompletedOrders,
        callback_methods=[EWrapper.completedOrder],
        done_method=EWrapper.completedOrdersEnd),
    ApiDefinition(request_method=EClient.reqExecutions,
        callback_methods=[EWrapper.execDetails],
        done_method=EWrapper.execDetailsEnd,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqFamilyCodes,
        callback_methods=[EWrapper.familyCodes]),
    ApiDefinition(request_method=EClient.reqGlobalCancel),
    ApiDefinition(request_method=EClient.reqHistoricalNews,
        callback_methods=[EWrapper.historicalNews],
        done_method=EWrapper.historicalNewsEnd,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqHistoricalTicks,
        callback_methods=[EWrapper.historicalTicks, EWrapper.historicalTicksBidAsk, EWrapper.historicalTicksLast],
        uses_req_id=True,
        has_done_flag=True),
    ApiDefinition(request_method=EClient.reqIds,
        callback_methods=[EWrapper.nextValidId]),
    ApiDefinition(request_method=EClient.reqManagedAccts,
        callback_methods=[EWrapper.managedAccounts]),
    ApiDefinition(request_method=EClient.reqMarketDataType),
    ApiDefinition(request_method=EClient.reqMarketRule,
        callback_methods=[EWrapper.marketRule]),
    ApiDefinition(request_method=EClient.reqMatchingSymbols,
        callback_methods=[EWrapper.symbolSamples]),
    ApiDefinition(request_method=EClient.reqMktDepthExchanges,
        callback_methods=[EWrapper.mktDepthExchanges]),
    ApiDefinition(request_method=EClient.reqNewsArticle,
        callback_methods=[EWrapper.newsArticle]),
    ApiDefinition(request_method=EClient.reqNewsProviders,
        callback_methods=[EWrapper.newsProviders]),
    ApiDefinition(request_method=EClient.reqScannerParameters,
        callback_methods=[EWrapper.scannerParameters]),
    ApiDefinition(request_method=EClient.reqSecDefOptParams,
        callback_methods=[EWrapper.securityDefinitionOptionParameter],
        done_method=EWrapper.securityDefinitionOptionParameterEnd,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqSmartComponents,
        callback_methods=[EWrapper.smartComponents],
        uses_req_id=True),
    ApiDefinition(request_method=EClient.reqSoftDollarTiers,
        callback_methods=[EWrapper.softDollarTiers],
        uses_req_id=True),
    ApiDefinition(request_method=EClient.requestFA,
        callback_methods=[EWrapper.receiveFA]),
    ApiDefinition(request_method=EClient.setServerLogLevel),
    ApiDefinition(request_method=EClient.subscribeToGroupEvents,
        callback_methods=[EWrapper.displayGroupUpdated],
        cancel_method=EClient.unsubscribeFromGroupEvents,
        is_subscription = True,
        uses_req_id=True),
    ApiDefinition(request_method=EClient.updateDisplayGroup),
    ApiDefinition(request_method=None, callback_methods=[EWrapper.commissionReport]),
    ApiDefinition(request_method=None, callback_methods=[EWrapper.marketDataType]),
    ApiDefinition(request_method=None, callback_methods=[EWrapper.orderBound, EWrapper.orderStatus,EWrapper.openOrder,EWrapper.openOrderEnd])
]

"""Missing or incomplete type aliases"""
OVERRIDDEN_TYPE_ALIASES = {
    'SetOfString': 'Set[str]',
    'SetOfFloat': 'Set[float]',
    'ListOfOrder': 'List[ibapi.order.Order]',
    'ListOfFamilyCode': 'List[ibapi.common.FamilyCode]',
    'ListOfContractDescription': 'List[ibapi.contract.ContractDescription]',
    'ListOfDepthExchanges': 'List[ibapi.common.DepthMktDataDescription]',
    'ListOfNewsProviders': 'List[ibapi.common.NewsProvider]',
    'HistogramDataList': 'List[ibapi.common.HistogramData]',
    'ListOfPriceIncrements': 'List[ibapi.common.PriceIncrement]',
    'ListOfHistoricalTick': 'List[ibapi.common.HistoricalTick]',
    'ListOfHistoricalTickBidAsk': 'List[ibapi.common.HistoricalTickBidAsk]',
    'ListOfHistoricalTickLast': 'List[ibapi.common.HistoricalTickLast]',
    'TagValueList': 'List[ibapi.tag_value.TagValue]',
    'SoftDollarTierList': 'List[ibapi.order.SoftDollarTier]',
    'SmartComponentMap': 'Dict[str, ibapi.common.SmartComponent]',
    'OrderId': 'int'
}

"""The types that are aliased by enums. This is used to generate conversion methods
to/from the enum type. These are strings to get the original class from globals"""
ENUM_ALIASES = {
    'TickType': 'TickTypeEnum',
    'FaDataType': 'FaDataTypeEnum',
    'MarketDataType': 'MarketDataTypeEnum',
}

""""""
REQUIRED_FIELDS = {
    'ibapi.common.BarData': {
        'close',
        'date',
        'high',
        'low',
        'open',
    }
}

"""Missing or incorrect hints for members"""
OVERRIDDEN_MEMBER_TYPE_HINTS = {
    'Order': {
        'algoParams': OVERRIDDEN_TYPE_ALIASES['TagValueList'],
        'conditions': 'List[ibapi.order_condition.OrderCondition]',
        'orderComboLegs': 'List[ibapi.order.OrderComboLeg]',
        'orderMiscOptions': 'List[ibapi.tag_value.TagValue]',
        'smartComboRoutingParams': OVERRIDDEN_TYPE_ALIASES['TagValueList'],
        'usePriceMgmtAlgo': 'bool'
    },
    'Contract': {
        'comboLegs': 'List[ibapi.contract.ComboLeg]',
        'deltaNeutralContract': 'ibapi.contract.DeltaNeutralContract'
    },
    'ContractDetails': {
        'secIdList': 'List[str]'
    },
    'ContractDescription': {
        'derivativeSecTypes': 'List[str]'
    },
    'SoftDollarTiers': {
        'tiers': 'List[ibapi.softdollartier.SoftDollarTier]'
    }
}

"""Missing or incorrect method signatures"""
OVERRIDDEN_METHOD_SIGNATURES = {
    'softDollarTiers': 'def softDollarTiers(self, reqId: int, tiers: SoftDollarTierList)'
}


import poloniex
import time
import datetime
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

def main():
    date, data = getDataPoloniex()
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(date, data)
    ax.set_title("BTC Price")
    ax.set_xlabel("Day")
    ax.set_ylabel("BTC Price[$]")
    plt.grid(fig)
    plt.show(fig)

def getDataPoloniex():
    polo = poloniex.Poloniex()
    polo.timeout = 2
    chartETH_BTC = polo.returnChartData('BTC_ETH', period=300, start=time.time() - 300 * 500, end=time.time())

    row_list = []
    for i in range(len(chartETH_BTC)):
        # date = datetime.datetime.fromtimestamp(chartETH_BTC[i]['date']).date()
        item = chartETH_BTC[i]
        dict = [ str(item['date']),  str(item['open']), str(item['high']),  str(item['low']), str(item['close']), str(item['volume'])]
        row_list.append(dict)
    print(row_list)
    df = pd.DataFrame(row_list)
    df.to_csv('polo.csv', index=False, header=False)

    # tmpDate = [chartETH_BTC[i]['date'] for i in range(len(chartETH_BTC))]
    # date = [datetime.datetime.fromtimestamp(tmpDate[i]).date() for i in range(len(tmpDate))]
    # data = [float(chartETH_BTC[i]['open']) for i in range(len(chartETH_BTC))]
    return

if __name__ == "__main__":
    getDataPoloniex()


import streamlit as st
import pandas as pd
from PIL import Image

st.set_page_config(layout='wide')

logo = Image.open('avengerslogo.png')
st.image(logo, width = 150)

df = pd.read_csv("charcters_stats.csv")

avengers = df[(df.Name == "Iron Man") | (df.Name == "Captain America") | (df.Name == "Spider-Man") | (df.Name == "Hulk") | (df.Name == "War Machine") | (df.Name == "Vision") | (df.Name == "Black Panther") | (df.Name == "Ant-Man")].reset_index(drop=True)

avenger_options = avengers['Name'].unique().tolist()
avenger = st.selectbox('Avengers', avenger_options,0)
row = avengers[avengers['Name']==avenger]

antMan = Image.open('antman.jpg')
blackPanther = Image.open('blackpanther.jpg')
captainAmerica = Image.open('captainamerica.jpg')
hulk = Image.open('hulk.jpg')
ironMan = Image.open('ironman.jpg')
spiderMan = Image.open('spiderman.jpg')
vision = Image.open('vision.jpg')
warMachine = Image.open('warmachine.jpg')

col1,col2,col3 = st.columns(3)
col2.metric('Stength',row.iloc[0]['Strength'])
col2.metric('Speed', row.iloc[0]['Speed'])
col3.metric('Power', row.iloc[0]['Power'])
col2.metric('Intelligence', row.iloc[0]['Intelligence'])
col3.metric('Durability', row.iloc[0]['Durability'])
col3.metric('Combat', row.iloc[0]['Combat'])
st.metric('Rating', row.iloc[:,2:7].mean(axis=1))
#put scatter plot here

if avenger == "Ant-Man":
    col1.image(antMan)
elif avenger == 'Black Panther':
    col1.image(blackPanther)
elif avenger == 'Captain America':
    col1.image(captainAmerica)
elif avenger == 'Hulk':
    col1.image(hulk)
elif avenger == 'Iron Man':
    col1.image(ironMan)
elif avenger == 'Spider-Man':
    col1.image(spiderMan)
elif avenger == 'Vision':
    col1.image(vision)
elif avenger == 'War Machine':
    col1.image(warMachine)


# -*- coding: UTF-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

__author__ = "d01"
__email__ = "jungflor@gmail.com"
__copyright__ = "Copyright (C) 2017, Florian JUNG"
__license__ = "MIT"
__version__ = "0.1.0"
__date__ = "2017-07-21"
# Created: 2017-07-21 13:38

import threading
import sys

import kombu.serialization
from kombu import Exchange, Connection, Queue, producers
from kombu.mixins import ConsumerMixin
from nameko.exceptions import (
    ContainerBeingKilled, MalformedRequest, MethodNotFound,
UnknownService, UnserializableValueError, deserialize, serialize)
from flotils import Loadable, StartStopable

class RPCListener(ConsumerMixin, Loadable, StartStopable):

    def __init__(self, settings=None):
        if settings is None:
            settings = {}
        ConsumerMixin.__init__(self)
        Loadable.__init__(self, settings)
        StartStopable.__init__(self, settings)

        self._broker_url = settings['AMQP_URI']
        self._heartbeat = settings.get('heartbeat', None)
        self._serializer = settings.get('serializer', "json")
        """ Which formats for serialization to support
            :type : list[unicode] """
        self._connection_timeout = settings.get('connection_timeout', 2.0)
        """ Timeout of timeout
            :type : float """
        self.service = settings.get('service')
        """ Service to call
            :type : object """
        self.allowed_functions = settings.get('allowed_functions')
        """ Which functions on the service are callable (None means all)
            :type : None | list[unicode] """
        self.connection = None
        """ Connection to broker
            :type : None | kombu.Connection """
        self._nameko_exchange_rpc_name = settings.get(
            'rpc_exchange', "nameko-rpc"
        )
        """ Name of exchange
            :type : unicode """
        self._nameko_exchange_rpc = None
        """ :type : None | kombu.Exchange """
        self._nameko_queue_rpc = None
        """ :type : None | kombu.Queue """
        self._service_name = settings['service_name']

    def _thread_wrapper(self, func, *args, **kwargs):
        """
        Wrap function for exception handling with threaded calls

        :param func: Function to call
        :type func: callable
        :param args: Tuple arguments
        :type args: ()
        :param kwargs: Dict arguments
        :type kwargs: dict
        :rtype: None
        """
        try:
            func(*args, **kwargs)
        except:
            self.exception("Threaded execution failed")

    def _register_service(self, service_name):
        if self._nameko_exchange_rpc is None:
            self._nameko_exchange_rpc = Exchange(
                self._nameko_exchange_rpc_name, type="topic",
                auto_delete=False, durable=True
            )
        self._nameko_queue_rpc = Queue(
            "rpc-{}".format(service_name),
            self._nameko_exchange_rpc,
            routing_key="{}.*".format(service_name),
            durable=True
        )

    def get_consumers(self, Consumer, channel):
        return [Consumer(
            queues=[self._nameko_queue_rpc],
            accept=[self._serializer],
            callbacks=[self._process_message]
        )]

    def _process_message(self, body, message):
        """
        Called whenever a message is received

        :param body: Decoded message body
        :type body: object
        :param message: Message instance
        :type message: kombu.message.Message
        :rtype: None
        """
        correlation_id = message.properties.get('correlation_id')
        routing_key = message.properties['reply_to']
        called = message.delivery_info.get('routing_key', "")
        exec_info = None
        error = None
        result = None
        args = kwargs = None

        if called:
            parts = called.split(".")
            if len(parts) == 2:
                called = parts[1]
            else:
                called = None
        if not called:
            self.error("No method called: '{}'".format(
                message.delivery_info.get('routing_key')
            ))
            error = serialize(MethodNotFound("No method given"))

        if self.service and called:
            message.ack()
            try:
                try:
                    args = body['args']
                    kwargs = body['kwargs']
                except KeyError:
                    raise MalformedRequest("Message missing `args` or `kwargs`")
                fn = None
                # Only allowed methods
                if self.allowed_functions is None \
                        or called in self.allowed_functions:
                    # Attempt to call
                    fn = getattr(self.service, called, None)
                    if callable(fn):
                        result = fn(*args, **kwargs)
                    else:
                        error = serialize(MethodNotFound(
                            "No such Method on service"
                        ))
                else:
                    error = serialize(MethodNotFound("Method not allowed"))
            except:
                self.exception("Failed to call process method")
                exec_info = sys.exc_info()
                result = None
        else:
            self.warning("No service found")
            message.ack()
            if not error:
                error = serialize(MethodNotFound("No service found"))

        if exec_info is not None:
            # TODO: Serialize
            try:
                error = kombu.serialization.dumps(exec_info, self._serializer)
            except:
                self.exception("Failed to serialize exec_info")
                error = "{}".format(exec_info)
        elif error is None:
            # Check that kombu is able to serialize data
            try:
                kombu.serialization.dumps(result, self._serializer)
            except:
                error = serialize(UnserializableValueError(result))
                result = None

        msg = {
            'result': result,
            'error': error
        }
        with producers[self.connection].acquire(
                block=True, timeout=self._connection_timeout
        ) as producer:
            producer.publish(
                msg,
                compression="bzip2",
                exchange=self._nameko_exchange_rpc_name,
                retry=True,
                routing_key=routing_key,
                serializer=self._serializer,
                correlation_id=correlation_id
            )

    def _run_worker(self):
        """
        Connect and run worker

        :rtype: None
        """
        self._register_service(self._service_name)
        with Connection(self._broker_url, heartbeat=self._heartbeat) as conn:
            self.connection = conn
            self.run()

    def start(self, blocking=False):
        super(RPCListener, self).start(False)
        if not blocking:
            try:
                a_thread = threading.Thread(
                    target=self._thread_wrapper,
                    args=(self._run_worker,)
                )
                a_thread.daemon = True
                a_thread.start()
            except:
                self.exception("Failed to run receive loop")
                self.stop()
                return
        else:
            self._run_worker()

    def stop(self):
        self.should_stop = True
        super(RPCListener, self).stop()
        self._nameko_queue_rpc = None
        self._nameko_queue_rpc = None


# author: Sebastian Coraccio
# date: 2017-05-10
# description:
# This Program will take a directory and make a image sheet out of the images
# within the folder.
#
# Images must be numbered so they are read in the correct order.
# Only png images can be in the directory
# All images need to have the same height and width

from PIL import Image
import os

def createSheet():
  image_files = []

  print("""Welcome to the Image Sheet Creator.
This Program will take a directory and make a image sheet out of the images
within the folder.
NOTE: Images must be numbered so they are read in the correct order.
NOTE: Only png images can be in the directory
NOTE: All images need to have the same height and width
""")

  directory = input("Enter the name of the directory which holds the .png files. ")

  # Get all files from the directory
  image_files = os.listdir(directory)

  # The first image will be used for default height and width values
  info_image = Image.open(directory + "/" + image_files[0])
  width = info_image.width
  height = info_image.height

  # Images are stitched together horizontally, so calculate the sheet width
  sheet_width = width * len(image_files)
  sheet_height = height

  sheet = Image.new('RGBA', (sheet_width, sheet_height))

  # Loop through the images and add them the sheet
  counter = 0
  for image in image_files:
    img = Image.open(directory + "/" + image)

    if not img.width == width and not img.height == height:
      print(image + " does not have the correct dimensions.")
      return

    sheet.paste(im=img, box=(width * counter,0))
    counter += 1

  file_name = directory + "_image_sheet.png"
  sheet.save(file_name)

  print("\nThe image sheet has been saved to " + file_name)
  print("Here are the sheetOptions for use with Corona SDK:\n")

  # Create the sheetOptions variable needed by Corona SDK
  corona_sheet = "local sheetOptions =\n{{\n\twidth = {0}\n\theight = {1}\n\tnumFrames = {2}\n}}".format(width, height, len(image_files))
  print(corona_sheet)

  print("\nAll done. Exiting Image Sheeter.")

if __name__ == "__main__":
  createSheet()


from aiogram import Dispatcher
from aiogram.dispatcher import FSMContext
from aiogram.types import (
    Message,
    CallbackQuery,
    InlineKeyboardButton,
    InlineKeyboardMarkup,
)

from bot import dp
from bot import bot
from app.middlewares import helpers
from app.states.department import Department
from app.database import departments as depart_db
from app.middlewares.checks import check_is_admin
from app.middlewares.state_check import state_check
from app.handlers.departments.depart_helper import *
from app.keyboards.inline.helper_buttons import back
from app.keyboards.inline import callback_datas as cd
from app.keyboards.inline import department_buttons as kb
from app.middlewares.helpers import call_chat_and_message

async def add_department(call: CallbackQuery, state: FSMContext):
    chat_id, message_id = await call_chat_and_message(call)
    await state_check(state)
    await Department.first()
    text = f"Введіть назву закладу"
    await bot.edit_message_text(
        chat_id=chat_id,
        message_id=message_id,
        text=text,
        reply_markup=kb.cancel_add_depart,
    )

async def depart_name(message: Message, state: FSMContext):
    name = message.text
    name = name.strip()
    depart_data = await state.get_data()
    if "name" in depart_data:
        await state.update_data(name=name)
        depart_data = await state.get_data()
        await Department.waiting_for_confirm.set()
        timetable = depart_data["timetable"]
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await message.answer(text, reply_markup=kb.confinm_depart)
    else:
        await state.update_data(name=name)
        await Department.next()
        text = f"Введіть область в якій знаходиться заклад"
        await message.answer(text)

async def depart_region(message: Message, state: FSMContext):
    region = message.text
    region = region.strip()
    depart_data = await state.get_data()
    if "region" in depart_data:
        await state.update_data(region=region)
        depart_data = await state.get_data()
        await Department.waiting_for_confirm.set()
        timetable = depart_data["timetable"]
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await message.answer(text, reply_markup=kb.confinm_depart)
    else:
        await state.update_data(region=region)
        await Department.next()
        text = f"Введіть місто у якому знаходиться заклад"
        await message.answer(text)

async def depart_city(message: Message, state: FSMContext):
    city = message.text
    city = city.strip()
    depart_data = await state.get_data()
    if "city" in depart_data:
        await state.update_data(city=city)
        depart_data = await state.get_data()
        await Department.waiting_for_confirm.set()
        timetable = depart_data["timetable"]
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await message.answer(text, reply_markup=kb.confinm_depart)
    else:
        await state.update_data(city=city)
        await Department.next()
        text = "Тепер введіть адресу закладу"
        await message.answer(text)

async def depart_address(message: Message, state: FSMContext):
    address = message.text
    address = address.strip()
    depart_data = await state.get_data()
    if "address" in depart_data:
        await state.update_data(address=address)
        depart_data = await state.get_data()
        await Department.waiting_for_confirm.set()
        timetable = depart_data["timetable"]
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await message.answer(text, reply_markup=kb.confinm_depart)
    else:
        await state.update_data(address=address)
        await Department.next()
        text = "Тепер введіть телефон/и закладу"
        await message.answer(text)

async def depart_phone(message: Message, state: FSMContext):
    phone = message.text
    phone = phone.strip()
    depart_data = await state.get_data()
    if "phone" in depart_data:
        await state.update_data(phone=phone)
        depart_data = await state.get_data()
        await Department.waiting_for_confirm.set()
        timetable = depart_data["timetable"]
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await message.answer(text, reply_markup=kb.confinm_depart)
    else:
        await state.update_data(phone=phone)
        await Department.next()
        text = "Тепер введіть ID адміністратора закладу"
        await message.answer(text)

async def depart_admin(message: Message, state: FSMContext):
    admin = message.text
    admin = int(admin)
    depart_data = await state.get_data()
    if "admin" in depart_data:
        await state.update_data(admin=admin)
        depart_data = await state.get_data()
        await Department.waiting_for_confirm.set()
        timetable = depart_data["timetable"]
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await message.answer(text, reply_markup=kb.confinm_depart)
    else:
        await state.update_data(admin=admin)
        await Department.next()
        text = (
            "Тепер введіть розклад закладу у порядку\n"
            "від понеділка до неділі наприклад:\n"
            "8:00-22:00|8:00-22:00|8:00-22:00|8:00-22:00|8:00-22:00|10:00-20:00|вихідний"
        )
        await message.answer(text)

async def depart_timetable(message: Message, state: FSMContext):
    try:
        timetable = message.text
        (
            monday,
            tuesday,
            wednesday,
            thursday,
            friday,
            saturday,
            sunday,
        ) = timetable.split("|")
        monday = monday.strip()
        tuesday = tuesday.strip()
        wednesday = wednesday.strip()
        thursday = thursday.strip()
        friday = friday.strip()
        friday = friday.strip()
        saturday = saturday.strip()
        sunday = sunday.strip()
        timetable = {
            "Понеділок": monday,
            "Вівторок": tuesday,
            "Середа": wednesday,
            "Четвер": thursday,
            "Пятниця": friday,
            "Субота": saturday,
            "Неділя": sunday,
        }
        depart_data = await state.get_data()
        if "timetable" in depart_data:
            await state.update_data(timetable=timetable)
            depart_data = await state.get_data()
            await Department.waiting_for_confirm.set()
        else:
            await state.update_data(timetable=timetable)
            depart_data = await state.get_data()
            await Department.next()
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await message.answer(text, reply_markup=kb.confinm_depart)
    except:
        await message.answer(
            "Введіть розклад закладу у порядку\n"
            "від понеділка до неділі наприклад:\n"
            "8:00-22:00|8:00-22:00|8:00-22:00|8:00-22:00|8:00-22:00|10:00-20:00|вихідний"
        )
        return

async def confirm_department(call: CallbackQuery, state: FSMContext):
    chat_id, message_id = await call_chat_and_message(call)
    if call["data"] == "depart_confirm:Yes":
        depart_data = await state.get_data()
        name = depart_data["name"]
        region = depart_data["region"]
        city = depart_data["city"]
        address = depart_data["address"]
        phone = depart_data["phone"]
        admin = depart_data["admin"]
        timetable = depart_data["timetable"]
        dp.loop.create_task(depart_db.add_department(
            name, region, city, address, phone, timetable, admin
        ))
        edit_depart = InlineKeyboardMarkup()
        edit_depart.add(back("depart_list"))
        await call.message.edit_reply_markup(reply_markup=None)
        await call.message.answer(
            f'Заклад "{name}" було додано', reply_markup=edit_depart
        )
        await state_check(state)

    elif call["data"] == "depart_confirm:No":
        text = "Оберіть що треба редагувати:"
        await bot.edit_message_text(
            chat_id=chat_id,
            message_id=message_id,
            text=text,
            reply_markup=kb.add_edit_depart,
        )

    elif call["data"] == "depart_add_edit:name":
        await Department.waiting_for_name.set()
        text = "Введіть нову назву закладу"
        await bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=text)

    elif call["data"] == "depart_add_edit:region":
        await Department.waiting_for_region.set()
        text = "Введіть нову область закладу"
        await bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=text)

    elif call["data"] == "depart_add_edit:city":
        await Department.waiting_for_city.set()
        text = "Введіть нове місто закладу"
        await bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=text)

    elif call["data"] == "depart_add_edit:address":
        await Department.waiting_for_address.set()
        text = "Введіть новий адрес закладу"
        await bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=text)

    elif call["data"] == "depart_add_edit:phone":
        await Department.waiting_for_phone.set()
        text = "Введіть новий телефон закладу"
        await bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=text)

    elif call["data"] == "depart_add_edit:timetable":
        await Department.waiting_for_timetable.set()
        text = "Введіть новий розклад закладу"
        await bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=text)

async def cancel(call: CallbackQuery, state: FSMContext):
    chat_id, message_id = await call_chat_and_message(call)
    text = "Відкинути всі зміни?"
    await bot.edit_message_text(
        chat_id=chat_id,
        message_id=message_id,
        text=text,
        reply_markup=kb.confirm_or_fail_cancel,
    )

async def fail_cancel(call: CallbackQuery, state: FSMContext):
    chat_id, message_id = await call_chat_and_message(call)
    my_state = await state.get_state()
    if my_state == "Department:waiting_for_name":
        text = "Введіть назву закладу"
        await bot.edit_message_text(
            chat_id=chat_id, message_id=message_id, text=text, reply_markup=None
        )
    elif my_state == "Department:waiting_for_confirm":
        depart_data = await state.get_data()
        timetable = depart_data["timetable"]
        week = await string_week(timetable)
        text = await string_confirm(depart_data, week)
        await bot.edit_message_text(
            chat_id=chat_id,
            message_id=message_id,
            text=text,
            reply_markup=kb.confinm_depart,
        )

def register_handlers_add_department(dp: Dispatcher):
    dp.register_callback_query_handler(
        add_department, cd.depart_menu_callback.filter(value=["add"]), state="*"
    )
    dp.register_message_handler(depart_name, state=Department.waiting_for_name)
    dp.register_message_handler(depart_region, state=Department.waiting_for_region)
    dp.register_message_handler(depart_city, state=Department.waiting_for_city)
    dp.register_message_handler(depart_address, state=Department.waiting_for_address)
    dp.register_message_handler(depart_phone, state=Department.waiting_for_phone)
    dp.register_message_handler(depart_admin, state=Department.waiting_for_admin)
    dp.register_message_handler(
        depart_timetable, state=Department.waiting_for_timetable
    )
    dp.register_callback_query_handler(
        confirm_department,
        cd.depart_confirm_callback.filter(value=["Yes", "No"]),
        state=Department.waiting_for_confirm,
    )
    dp.register_callback_query_handler(
        confirm_department,
        cd.depart_add_edit_callback.filter(
            field=["name", "region", "city", "address", "phone", "timetable"]
        ),
        state=Department.waiting_for_confirm,
    )
    dp.register_callback_query_handler(
        cancel, cd.depart_confirm_callback.filter(value=["Cancel"]), state="*"
    )
    dp.register_callback_query_handler(
        fail_cancel, cd.depart_confirm_callback.filter(value=["Not"]), state="*"
    )