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

from _version import __version__
from calendar import month_name
from datetime import date, datetime
from decimal import Decimal
from tkcalendar import DateEntry
from tkinter import ttk, messagebox
from tkinter.filedialog import askopenfile
from shutil import copy, copy2
from pathlib import Path
from tkinter import filedialog as fd
import tkinter.font as tkFont
from tkHyperlinkManager import HyperlinkManager
from math import floor
from xl import export_to_excel
import datetime as dt
import locale
import os, zlib, sys
import tkinter as tk
import ast

# example of subsription and default recipient
# EMAIL_TO = b'\xd0\xa4\xd0\xbe\xd0\xb7\xd0\xb7\xd0\xb8|\
# \xd0\x9b\xd0\xbe\xd0\xb3\xd0\xb8\xd1\x81\xd1\x82\xd0\xb8\xd0\xba\xd0\xb0|\
# \xd0\x90\xd0\xbd\xd0\xb0\xd0\xbb\xd0\xb8\xd1\x82\xd0\xb8\xd0\xba\xd0\xb8'.decode()
EMAIL_TO = zlib.decompress(b'x\x9c\xbb\xb0\xe4\xc2\xbe\x0b\xdb\x81pG\xcd\x85'
                           b'\xd9@\xe6\xe6\x0b;.6^l\xba\xb0\xe3\xc2\xae\x0b'
                           b'\x1bj.\xcc\xb9\xb0\x01\xcc\xddz\xb1\xe1\xc2\x14 '
                           b'\xbb\xe9\xc2\x06\x00\xa0e#\xea').decode()
# example of path to independent report
TEMPLATE_PATH = zlib.decompress(b'x\x9c\x8b\x89I\xcb\xaf\xaa\xaa\xd4\xcbI\xcc'
                                b'\x8bq\xc9O.\xcdM\xcd+)\x8e\xf1\xc8\xcfI\xc9'
                                b'\xccK\x8fqI-H,*\x81\x88\x05g\xe6\x14\xe4\xc7'
                                b'\\\x98}a\xdf\x85\xcd\x17v\\l\xbc\xd8ta\xc7\x85]'
                                b'\x176\xc4\xbb\xbb\x06\xf9\xba\x06\xc7\x04\xa4'
                                b'\x16\xa5\xe5\x17\xe5\xa6\x16\xc5\x94\xa4&g\xc4'
                                b'\x04\xa5&\x17\x95f\x96\x00M\x01\x00\xa74-'
                                b'\x18').decode()

UPLOAD_PATH = zlib.decompress(b"x\x9c\x8b\x89I\xcb\xaf\xaa\xaa\xd4\xcbI\xcc"
                              b"\x8bq\xc9O.\xcdM\xcd+)\x8e\xf1\xc8\xcfI\xc9"
                              b"\xccK\x8fqI-H,*\x81\x88\xf9\xe4\xa7g\x16"
                              b"\x97df'\xc6x\x04\xc5\x17\xa5&\x17\x95\x96\x00"
                              b"\x95\x00\x00<\xcb\x19\xa1").decode()

# UPLOAD_PATH = zlib.decompress(b'x\x9c\x8b\x89I\xcb\xaf\xaa\xaa\xd4\xcbI\xcc'
#                               b'\x8bq\xc9O.\xcdM\xcd+)\x8e\t\xce\xcc)\xc8\x0f'
#                               b'\xcbLI\xcd\x07\x00\xd2\x13\x0c\xcc').decode()

DOWNLOAD_PATH = zlib.decompress(b'x\x9c\x8b\x89I\xcb\xaf\xaa\xaa\xd4\xcbI\xcc\x8bq\xc9O.\xcdM'
            b'\xcd+)\x8e\xf1\xc8\xcfI\xc9\xccK\x8fqI-H,*\x81\x88\x05g\xe6'
            b'\x14\xe4\xc7\\\x98}a\xdf\x85\xcd\x17v\\l\xbc\xd8ta\xc7\x85]'
            b'\x176\xc4\xbb\xbb\x06\xf9\xba\x06\xc7\x04\xa4\x16\xa5\xe5\x17'
            b'\xe5\xa6\x16\xc5\x94\xa4&g\xc4\x04\xa5&\x17\x95f\x96\x00M\x01'
            b'\x00\xa74-\x18').decode()

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

class IncorrectFloatError(PaymentsError):
    """ Exception raised if sum is not converted to float.

    Attributes:
        expression - input expression in which the error occurred;
        message - explanation of the error.
    """

    def __init__(self, expression, message='Введена некорректная сумма'):
        self.expression = expression
        self.message = message
        super().__init__(self.expression, self.message)

class MonthFilterError(PaymentsError):
    """ Exception raised if month don't chosen in filter.

    Attributes:
        expression - input expression in which the error occurred;
        message - explanation of the error.
    """

    def __init__(self, expression, message='Не выбран месяц'):
        self.expression = expression
        self.message = message
        super().__init__(self.expression, self.message)

class AccessError(tk.Tk):
    """ Raise an error when user doesn't have permission to work with app.
    """

    def __init__(self):
        super().__init__()
        self.withdraw()  # Do not show main window
        messagebox.showerror(
            'Ошибка доступа',
            'Нет прав для работы с приложением.\n'
            'Для получения прав обратитесь на рассылку ' + EMAIL_TO
        )
        self.destroy()

class LoginError(tk.Tk):
    """ Raise an error when user doesn't have permission to work with db.
    """

    def __init__(self):
        super().__init__()
        self.withdraw()  # Do not show main window
        messagebox.showerror(
            'Ошибка подключения',
            'Нет прав для работы с сервером.\n'
            'Обратитесь на рассылку ' + EMAIL_TO
        )
        self.destroy()

class NetworkError(tk.Tk):
    """ Raise a message about network error.
    """

    def __init__(self):
        super().__init__()
        self.withdraw()  # Do not show main window
        messagebox.showerror(
            'Ошибка cети',
            'Возникла общая ошибка сети.\nПерезапустите приложение'
        )
        self.destroy()

class UploadError(tk.Tk):
    """ Raise an error when user doesn't have permission to upload folder.
    """

    def __init__(self):
        super().__init__()
        self.withdraw()  # Do not show main window
        messagebox.showerror(
            'Ошибка загрузки файла',
            'Нет прав доступа к каталогу загрузки.\n'
            'Для получения прав обратитесь на рассылку ' + EMAIL_TO
        )
        self.destroy()

class FileNotFound(tk.Tk):
    """ Raise an error when user doesn't have permission to upload folder.
    """

    def __init__(self):
        super().__init__()
        self.withdraw()  # Do not show main window
        messagebox.showerror(
            'Ошибка открытия файла',
            'Требуемый файл не найден.\n'
        )
        self.destroy()

class RestartRequiredAfterUpdateError(tk.Tk):
    """ Raise a message about restart needed after update.
    """

    def __init__(self):
        super().__init__()
        self.withdraw()  # Do not show main window
        messagebox.showinfo(
            'Необходима перезагрузка',
            'Выполнено критическое обновление.\nПерезапустите приложение'
        )
        self.destroy()

class UnexpectedError(tk.Tk):
    """ Raise a message when an unexpected exception occurs.
    """

    def __init__(self, *args):
        super().__init__()
        self.withdraw()  # Do not show main window
        messagebox.showinfo(
            'Непредвиденное исключение',
            'Возникло непредвиденное исключение\n' + '\n'.join(map(str, args))
        )
        self.destroy()

class StringSumVar(tk.StringVar):
    """ Contains function that returns var formatted in a such way, that
        it can be converted into a float without an error.
    """

    def get_float_form(self, *args, **kwargs):
        return super().get(*args, **kwargs).replace(' ', '').replace(',', '.')

class RecruitingApp(tk.Tk):
    def __init__(self, **kwargs):
        super().__init__()
        self.title('Заявки на персонал')
        self.iconbitmap(os.path.join(sys.path[0],'resources/file.ico'))
        # store the state of PreviewForm
        self.state_PreviewForm = 'normal'
        # geometry_storage {Framename:(width, height)}
        self._geometry = {'PreviewForm': (1340, 550),
                          'CreateForm': (480, 440),
                          'UpdateForm': (480, 300)}
        # Virtual event for creating request
        self.event_add("<<create>>", "<Control-S>", "<Control-s>",
                       "<Control-Ucircumflex>", "<Control-ucircumflex>",
                       "<Control-twosuperior>", "<Control-threesuperior>",
                       "<KeyPress-F5>")
        self.bind_all("<Key>", self._onKeyRelease, '+')
        self.bind("<<create>>", self._create_request)
        self.active_frame = None
        # handle the window close event
        self.protocol("WM_DELETE_WINDOW", self._quit)
        # hide until all frames have been created
        self.withdraw()
        # To import months names in cyrillic
        locale.setlocale(locale.LC_ALL, 'RU')
        # Customize header style (used in PreviewForm)
        style = ttk.Style()
        try:
            style.element_create("HeaderStyle.Treeheading.border", "from",
                                 "default")
            style.layout("HeaderStyle.Treeview.Heading", [
                ("HeaderStyle.Treeheading.cell", {'sticky': 'nswe'}),
                ("HeaderStyle.Treeheading.border",
                 {'sticky': 'nswe', 'children': [
                     ("HeaderStyle.Treeheading.padding",
                      {'sticky': 'nswe', 'children': [
                          ("HeaderStyle.Treeheading.image",
                           {'side': 'right', 'sticky': ''}),
                          ("HeaderStyle.Treeheading.text", {'sticky': 'we'})
                      ]})
                 ]}),
            ])
            style.configure("HeaderStyle.Treeview.Heading",
                            background="#dddddd", foreground="black",
                            relief='groove', font=('Arial', 8))
            style.map("HeaderStyle.Treeview.Heading",
                      relief=[('active', 'sunken'), ('pressed', 'flat')])

            style.map('ButtonGreen.TButton')
            style.configure('ButtonGreen.TButton', foreground='green')

            style.map('ButtonRed.TButton')
            style.configure('ButtonRed.TButton', foreground='red')

            style.configure("TMenubutton", background='white')
        except tk.TclError:
            # if during debug previous tk wasn't destroyed
            # and style remains modified
            pass

        # the container is where we'll stack a bunch of frames
        # the one we want to be visible will be raised above others
        container = tk.Frame(self)
        container.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self._frames = {}
        for F in (PreviewForm, CreateForm, UpdateForm):
            frame_name = F.__name__
            frame = F(parent=container, controller=self, **kwargs)
            self._frames[frame_name] = frame
            # put all of them in the same location
            frame.grid(row=0, column=0, sticky='nsew')

        self._show_frame('PreviewForm')
        # restore after withdraw
        self.deiconify()

    def _center_window(self, w, h):
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()

        start_x = int((screen_width / 2) - (w / 2))
        start_y = int((screen_height / 2) - (h * 0.55))

        self.geometry('{}x{}+{}+{}'.format(w, h, start_x, start_y))

    def _fill_UpdateForm(self, **kwargs):
        """ Control function to transfer data from Preview- to CreateForm. """
        id = kwargs['ID']
        internalID = kwargs['Номер заявки']
        officeName = kwargs['Офис']
        departmentName = kwargs['Департамент']
        responsibleUser = kwargs['Ответственный']
        statusID = kwargs['StatusID']
        fileCV = kwargs['Файл резюме']
        startWork = kwargs['Дата выхода']
        frame = self._frames['UpdateForm']
        frame._fill_from_UpdateForm(id, internalID, officeName, departmentName,
                                    responsibleUser, statusID, fileCV, startWork)

    def _onKeyRelease(*args):
        event = args[1]
        # check if Ctrl pressed
        if not (event.state == 12 or event.state == 14):
            return
        if event.keycode == 88 and event.keysym.lower() != 'x':
            event.widget.event_generate("<<Cut>>")
        elif event.keycode == 86 and event.keysym.lower() != 'v':
            event.widget.event_generate("<<Paste>>")
        elif event.keycode == 67 and event.keysym.lower() != 'c':
            event.widget.event_generate("<<Copy>>")

    def _show_frame(self, frame_name):
        """ Show a frame for the given frame name. """
        if frame_name == 'CreateForm' or frame_name == 'UpdateForm':
            # since we have only two forms, when we activating CreateForm
            # we know by exception that PreviewForm is active
            self.state_PreviewForm = self.state()
            self.state('normal')
            self.resizable(width=False, height=False)
        else:
            self.state(self.state_PreviewForm)
            self.resizable(width=True, height=True)
        frame = self._frames[frame_name]
        frame.tkraise()
        self._center_window(*(self._geometry[frame_name]))
        # Make sure active_frame changes in case of network error
        try:
            if frame_name in ('PreviewForm'):
                frame._resize_columns()
                frame._refresh()
                # Clear form in CreateFrom and UpdateForm by autofill form
                self._frames['CreateForm']._clear(0)
                self._frames['UpdateForm']._clear()
        finally:
            self.active_frame = frame_name

    def _create_request(self, event):
        """
        Creates request when hotkey is pressed if active_frame is CreateForm.
        """
        if self.active_frame == 'CreateForm':
            self._frames[self.active_frame]._create_request()

    def _quit(self):
        if self.active_frame != 'PreviewForm':
            self._show_frame('PreviewForm')
        elif messagebox.askokcancel("Выход", "Выйти из приложения?"):
            # destroy app
            self.destroy()

class RecruitingFrame(tk.Frame):
    def __init__(self, parent, controller, connection, user_info, office):
        super().__init__(parent)
        self.parent = parent
        self.controller = controller
        self.conn = connection
        self.office = {}
        if isinstance(self, PreviewForm):
            self.officeID, self.office = zip(*[(None, 'Все'), ] + office)
        self.user_info = user_info
        # Often used info
        self.userID = user_info.UserID
        self.userOffice = user_info.OfficeName
        self.userDepartment = user_info.DepartmentName
        self.userPosition = user_info.Position

    def _add_user_label(self, parent):
        """ Adds user name in top right corner. """
        user_label = tk.Label(parent, text='Пользователь: ' +
                                           self.user_info.ShortUserName + '  Версия ' + __version__,
                              font=('Arial', 8))
        user_label.pack(side=tk.RIGHT, anchor=tk.NE)

    def get_officeID(self, office):
        return self.office[office][0]

    def exit_user(self):
        pass

class CreateForm(RecruitingFrame):
    def __init__(self, parent, controller, connection, user_info, office,
                 **kwargs):
        super().__init__(parent, controller, connection, user_info, office)
        self.uploaded_filename = str()
        # Top Frame with description and user name
        top = tk.Frame(self, name='top_cf', padx=5)
        self.main_label = tk.Label(top,
                                   text='Форма создания заявки на поиск персонала',
                                   padx=10, pady=5,
                                   font=('Calibri', 11, 'bold'))
        self._top_pack()

        # First Fill Frame
        row1_cf = tk.Text(self, padx=18, height=3, relief=tk.FLAT, bg='#f1f1f1')
        row1_cf.insert(tk.INSERT, 'Подразделение инициатора:')
        row1_cf.insert(tk.INSERT, str('\n' + self.userOffice))
        row1_cf.insert(tk.INSERT, str('\n' + self.userDepartment))
        row1_cf.tag_add('title', 1.0, '1.end')
        row1_cf.tag_add('style', 2.0, '2.end')
        row1_cf.tag_add('style', 3.0, '3.end')
        row1_cf.tag_config('title', font=("Calibri", 10, 'bold'),
                           justify=tk.LEFT)
        row1_cf.tag_config('style', font=("Calibri", 10, 'normal'),
                           justify=tk.LEFT)
        row1_cf.configure(state="disabled")

        self._row1_pack()

        # Second Fill Frame
        row2_cf = tk.Frame(self, name='row2_cf', padx=10)
        self.separator = ttk.Separator(row2_cf, orient='horizontal')

        self._row2_pack()

        # Third Fill Frame
        row3_cf = tk.Frame(self, name='row3_cf', padx=10)
        self.requirements_label = tk.Label(row3_cf,
                                           text='1. Откройте и заполните файл требований:',
                                           padx=8)
        bt_open_file = ttk.Button(row3_cf, text="Открыть", width=20,
                                  command=self.open_file_requirements)
        bt_open_file.pack(side=tk.RIGHT, padx=15, pady=0)

        self._row3_pack()

        # Fourth Fill Frame
        row4_cf = tk.Frame(self, name='row4_cf', padx=10)
        self.attach_label = tk.Label(row4_cf,
                                     text='2. Прикрепите файл требований:',
                                     padx=8)
        self.upload_btn_text = tk.StringVar()
        bt_upload = ttk.Button(row4_cf, textvariable=self.upload_btn_text,
                               width=20,
                               command=self._upload_requirements,
                               style='ButtonGreen.TButton')
        self.upload_btn_text.set("Выбрать файл")
        bt_upload.pack(side=tk.RIGHT, padx=15, pady=0)

        self._row4_pack()

        # Fifth Fill Frame
        row5_cf = tk.Frame(self, name='row5_cf', padx=10)
        self.candidatePositionLabel = tk.Label(row5_cf,
                                               text='Название должности кандидата:',
                                               padx=7)
        self.candidatePositionEntry = tk.Entry(row5_cf, width=40)

        self._row5_pack()

        # Six Fill Frame
        row6_cf = tk.Frame(self, name='row6_cf', padx=10)
        self.plannedClosingDateLabel = tk.Label(row6_cf,
                                                text='Плановая дата закрытия заявки:',
                                                padx=7)
        self.plannedClosingDate = tk.StringVar()
        self.plannedClosingDateEntry = DateEntry(row6_cf, width=16,
                                                 state='readonly',
                                                 textvariable=self.plannedClosingDate,
                                                 font=('Arial', 9),
                                                 selectmode='day',
                                                 borderwidth=2,
                                                 locale='ru_RU')

        # Text Frame
        text_cf = ttk.LabelFrame(self, text=' Комментарий к заявке ',
                                 name='text_cf')

        self.customFont = tkFont.Font(family="Arial", size=10)
        self.desc_text = tk.Text(text_cf,
                                 font=self.customFont)  # input and output box
        self.desc_text.configure(width=100)
        self.desc_text.pack(in_=text_cf, expand=True)

        self._row6_pack()

        # Bottom Frame with buttons
        bottom_cf = tk.Frame(self, name='bottom_cf')

        bt3 = ttk.Button(bottom_cf, text="Назад", width=10,
                         command=lambda: controller._show_frame('PreviewForm'))
        bt3.pack(side=tk.RIGHT, padx=15, pady=10)

        bt2 = ttk.Button(bottom_cf, text="Очистить", width=10,
                         command=lambda: self._clear(1),
                         style='ButtonRed.TButton')
        bt2.pack(side=tk.RIGHT, padx=0, pady=0)

        bt1 = ttk.Button(bottom_cf, text="Создать заявку", width=15,
                         command=self._create_request,
                         style='ButtonGreen.TButton')
        bt1.pack(side=tk.RIGHT, padx=15, pady=10)

        # Pack frames
        top.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        bottom_cf.pack(side=tk.BOTTOM, fill=tk.X)
        row1_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row2_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row3_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row4_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row5_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row6_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        text_cf.pack(side=tk.TOP, fill=tk.X, expand=True, padx=15, pady=15)

    def open_file_requirements(self):
        pathToFile = DOWNLOAD_PATH + "\\" + 'Требования.xlsb'
        return os.startfile(pathToFile)

    def _upload_requirements(self):
        filename = fd.askopenfilename(filetypes=[("Excel files", ".xlsb")])
        if filename:
            # Rename file while uploading
            now = str(datetime.now())[:19]
            now = now.replace(":", "_")
            now = now.replace(" ", "_")
            new_filename = "Требования_" + now + ".xlsb"
            distinationPath = UPLOAD_PATH + "\\" + new_filename
            try:
                copy(filename, distinationPath)
                path = Path(distinationPath)
                self.uploaded_filename = path.name
                self.upload_btn_text.set("Файл добавлен")
            except PermissionError:
                UploadError()

    def _remove_uploaded_file(self):
        os.remove(UPLOAD_PATH + '\\' + self.uploaded_filename)

    def _clear(self, param):
        self.candidatePositionEntry.configure(state="normal")
        self.candidatePositionEntry.delete(0, tk.END)
        self.desc_text.delete("1.0", tk.END)
        self.upload_btn_text.set("Выбрать файл")
        self.upload_filename = str()
        self.plannedClosingDateEntry.set_date(datetime.now())
        if self.uploaded_filename and param == 1:
            self._remove_uploaded_file()

    def _fill_from_PreviewForm(self):
        pass

    def _convert_date(self, date, output=None):
        """ Take date and convert it into output format.
            If output is None datetime object is returned.

            date: str in format '%d[./]%m[./]%y' or '%d[./]%m[./]%Y'.
            output: str or None, output format.
        """
        date = date.replace('/', '.')
        try:
            dat = datetime.strptime(date, '%d.%m.%y')
        except ValueError:
            dat = datetime.strptime(date, '%d.%m.%Y')
        if output:
            return dat.strftime(output)
        return dat

    def _create_request(self):
        messagetitle = 'Создание заявки'
        is_validated = self._validate_request_creation(messagetitle)
        if not is_validated:
            return

        request = {'userID': self.userID,
                   'positionName': self.candidatePositionEntry.get(),
                   'plannedDate': self._convert_date(
                       self.plannedClosingDateEntry.get()),
                   'fileRequirements': self.uploaded_filename,
                   'commentText': self.desc_text.get("1.0", tk.END).strip()

                   }

        created_success = self.conn.create_request(**request)
        if created_success == 1:
            messagebox.showinfo(
                messagetitle, 'Заявка на поиск персонала создана'
            )
            self._clear(0)
            self.controller._show_frame('PreviewForm')
        else:
            self._remove_uploaded_file()
            messagebox.showerror(
                messagetitle, 'Произошла ошибка при добавлении заявки'
            )

    def _convert_str_date(self, date):
        """ Take str and convert it into date format.
            date: str in format '%d[./]%m[./]%y' or '%d[./]%m[./]%Y'.
        """
        date_time_str = date
        date_time_obj = dt.datetime.strptime(date_time_str, '%Y-%m-%d')
        return date_time_obj.date()

    def _row1_pack(self):
        pass

    def _row2_pack(self):
        self.separator.pack(fill='x')

    def _row3_pack(self):
        self.requirements_label.pack(side=tk.LEFT, padx=0)

    def _row4_pack(self):
        self.attach_label.pack(side=tk.LEFT, padx=0)

    def _row5_pack(self):
        self.candidatePositionLabel.pack(side=tk.LEFT)
        self.candidatePositionEntry.pack(side=tk.LEFT, padx=2)

    def _row6_pack(self):
        self.plannedClosingDateLabel.pack(side=tk.LEFT)
        self.plannedClosingDateEntry.pack(side=tk.LEFT, padx=3)

    def _top_pack(self):
        self.main_label.pack(side=tk.TOP, expand=False, anchor=tk.NW)

    def _validate_request_creation(self, messagetitle):
        """ Check if all fields are filled properly. """
        if not self.uploaded_filename:
            messagebox.showerror(
                messagetitle, 'Вы не загрузили файл требований'
            )
            return False
        if not self.candidatePositionEntry.get():
            messagebox.showerror(
                messagetitle, 'Не указана должность вакансии'
            )
            return False
        if self._convert_date(self.plannedClosingDateEntry.get()).date() == datetime.now().date():
            messagebox.showerror(
                messagetitle, 'Плановая дата закрытия заявки не может быть равна текущей дате'
            )
            return False
        return True

class UpdateForm(RecruitingFrame):
    def __init__(self, parent, controller, connection, user_info, office,
                 responsible_all, **kwargs):
        super().__init__(parent, controller, connection, user_info, office)
        self.responsibleID, self.responsible = zip(
            *[(0, 'Не назначен'), ] + responsible_all)
        self.responsible_choice = (dict(responsible_all))
        self.customStatusID, self.customStatusName = zip(*[(0, 'Не выбрано'),
                                                           (
                                                           4, 'Верифицировать'),
                                                           (2,
                                                            'Вернуть в работу')])
        self.UserID = self.user_info.UserID
        self.isSuperHR = self.user_info.isSuperHR
        self.filenameCV = str()
        self.responsible_choice_list = []
        # Top Frame with description and user name
        top = tk.Frame(self, name='top_cf', padx=5)
        self.main_label = tk.Label(top,
                                   text='Форма управления заявкой',
                                   padx=10, font=('Calibri', 11, 'bold'))
        self._top_pack()

        # First Fill Frame
        row1_cf = tk.Frame(self, name='row1_cf', padx=15)
        self.request_info_text = tk.StringVar()
        self.request_info_label = tk.Label(row1_cf,
                                           textvariable=self.request_info_text,
                                           padx=7, justify=tk.LEFT,
                                           font=('Calibri', 10))

        self._row1_pack()

        # Second Fill Frame
        row2_cf = tk.Frame(self, name='row2_cf', padx=15)
        self.separator = ttk.Separator(row2_cf, orient='horizontal')

        self._row2_pack()

        # Third Fill Frame
        row3_cf = tk.Frame(self, name='row3_cf', padx=15)
        self.responsible_label = tk.Label(row3_cf,
                                          text='Ответственный за выполнение заявки:',
                                          padx=7)
        self.menubutton_text = tk.StringVar()
        self.menubutton = tk.Menubutton(row3_cf, textvariable=self.menubutton_text,
                                        indicatoron=True, borderwidth=1,
                                        relief="raised")
        self.menu_choice_responsible = tk.Menu(self.menubutton, tearoff=False)
        self.menubutton_text.set("Выбрать из списка")
        self.menubutton.configure(menu=self.menu_choice_responsible)

        self.choices = {}
        for choice in self.responsible_choice.values():
            self.choices[choice] = tk.IntVar(value=0)
            self.menu_choice_responsible.add_checkbutton(label=choice,
                                                 variable=self.choices[choice],
                                                 onvalue=1, offvalue=0,
                                                 command=self._responsible_choice_list)

        self._row3_pack()

        # Fourth Fill Frame
        row4_cf = tk.Frame(self, name='row4_cf', padx=15)
        self.attach_label = tk.Label(row4_cf,
                                     text='Резюме согласованного кандидата:',
                                     padx=8)
        self.upload_btn_text = tk.StringVar()
        self.bt_upload = ttk.Button(row4_cf, textvariable=self.upload_btn_text,
                                    width=23,
                                    command=self._upload_cv,
                                    style='ButtonGreen.TButton',
                                    state=tk.NORMAL)
        self.upload_btn_text.set("Выбрать файл")
        self.bt_upload.pack(side=tk.RIGHT, padx=15, pady=0)

        self._row4_pack()

        # Fifth Fill Frame
        row5_cf = tk.Frame(self, name='row5_cf', padx=15)
        self.plannedDateStartWorklabel = tk.Label(row5_cf,
                                                  text='Ожидаемая дата выхода на работу:',
                                                  padx=8)
        self.plannedDateStartWork = tk.StringVar()
        self.plannedDateStartWorkEntry = DateEntry(row5_cf, width=17,
                                                   state='readonly',
                                                   textvariable=self.plannedDateStartWork,
                                                   font=('Arial', 9),
                                                   selectmode='day',
                                                   borderwidth=2,
                                                   locale='ru_RU')

        self._row5_pack()

        # Six Fill Frame
        row6_cf = tk.Frame(self, name='row6_cf', padx=15)
        self.status_label = tk.Label(row6_cf,
                                     text='Верифицировать или вернуть в работу:',
                                     padx=7)
        self.status_box = ttk.Combobox(row6_cf, width=20,
                                       state='readonly')
        self.status_box['values'] = self.customStatusName

        self._row6_pack()

        # Bottom Frame with buttons
        bottom_cf = tk.Frame(self, name='bottom_cf')

        bt3 = ttk.Button(bottom_cf, text="Назад", width=10,
                         command=lambda: controller._show_frame('PreviewForm'))
        bt3.pack(side=tk.RIGHT, padx=15, pady=10)

        bt1 = ttk.Button(bottom_cf, text="Сохранить", width=15,
                         command=self._update_vacancy,
                         style='ButtonGreen.TButton')
        bt1.pack(side=tk.RIGHT, padx=0, pady=0)

        # Pack frames
        top.pack(side=tk.TOP, fill=tk.BOTH)
        bottom_cf.pack(side=tk.BOTTOM, fill=tk.X)
        row1_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row2_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row3_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row4_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row5_cf.pack(side=tk.TOP, fill=tk.X, pady=5)
        row6_cf.pack(side=tk.TOP, fill=tk.X, pady=5)

    def _responsible_choice_list(self):
        self.responsible_choice_list = []
        for name, var in self.choices.items():
            if var.get() == 1:
                self.responsible_choice_list.append(self.getUserID(name))

    # Deselect checked row in menu (destroy and create menubutton again)
    def _deselect_checked_responsible(self):
        self.responsible_choice_list.clear()
        self.menu_choice_responsible.destroy()
        self.menu_choice_responsible = tk.Menu(self.menubutton, tearoff=False)
        self.menubutton.configure(menu=self.menu_choice_responsible)
        for choice in self.responsible_choice.values():
            self.choices[choice] = tk.IntVar(value=0)
            self.menu_choice_responsible.add_checkbutton(label=choice,
                                                 variable=self.choices[choice],
                                                 onvalue=1, offvalue=0,
                                                 command=self._responsible_choice_list)

    def getUserID(self, items):
        UserID = int
        for k,v in self.responsible_choice.items():
            if v == items:
                UserID = k
        return UserID

    def _upload_cv(self):
        filename = fd.askopenfilename()
        if filename:
            # Rename file while it uploading
            file = Path(filename).name
            now = str(datetime.now())[:19]
            now = now.replace(":", "_")
            now = now.replace(" ", "_")
            new_filename = file.replace(".", '_' + now + '.')
            distinationPath = UPLOAD_PATH + "\\" + new_filename
            try:
                copy(filename, distinationPath)
                path = Path(distinationPath)
                self.upload_filename = path.name
                self.upload_btn_text.set("Файл добавлен")
            except PermissionError:
                UploadError()

    def _remove_uploaded_file(self):
        os.remove(UPLOAD_PATH + '\\' + self.filenameCV)

    def _clear(self):
        self.plannedDateStartWorkEntry.set_date(datetime.now())
        self.upload_filename = str()
        self._deselect_checked_responsible()

    def _fill_from_UpdateForm(self, id, internalID, officeName,
                              departmentName, responsibleUser, statusID,
                              fileCV, startWork):
        """ When button "Управление заявкой" from PreviewForm is activated,
        fill some fields taken from choosed in PreviewForm request.
        """
        self.request_id = id
        # self.responsible_box.set(responsibleUser)
        self.filenameCV = fileCV
        if not self.isSuperHR:
            self.menubutton.configure(state="disabled")
        self.request_info_text.set('Номер заявки: ' + internalID + '\n' +
                                   'Офис : ' + officeName + '\n' +
                                   'Подразделение: ' + departmentName
                                   )
        if statusID in (1, 4):
            self.bt_upload.config(state=tk.DISABLED)
            self.status_box.configure(state="disabled")
            self.upload_btn_text.set("Выбрать файл")
            self.plannedDateStartWorkEntry.config(state=tk.DISABLED)
            if self.isSuperHR:
                self.menubutton.configure(state="normal")
        elif statusID == 2:
            self.bt_upload.config(state=tk.NORMAL)
            self.status_box.config(state=tk.DISABLED)
            self.upload_btn_text.set("Выбрать файл")
            self.plannedDateStartWorkEntry.configure(state="readonly")
            if self.isSuperHR:
                self.menubutton.configure(state="normal")
        elif statusID == 3:
            self.bt_upload.config(state=tk.DISABLED)
            self.upload_btn_text.set("Файл добавлен")
            self.status_box.configure(state="readonly")
            self.plannedDateStartWorkEntry.set_date(
                self._convert_str_date(startWork))
            self.plannedDateStartWorkEntry.config(state=tk.DISABLED)
            if self.isSuperHR:
                self.menubutton.configure(state="disabled")
        self.status_box.set('Не выбрано')

    def _convert_date(self, date, output=None):
        """ Take date and convert it into output format.
            If output is None datetime object is returned.

            date: str in format '%d[./]%m[./]%y' or '%d[./]%m[./]%Y'.
            output: str or None, output format.
        """
        date = date.replace('/', '.')
        try:
            dat = datetime.strptime(date, '%d.%m.%y')
        except ValueError:
            dat = datetime.strptime(date, '%d.%m.%Y')
        if output:
            return dat.strftime(output)
        return dat

    def _update_vacancy(self):
        messagetitle = 'Изменение заявки'
        is_validated = self._validate_request_creation(messagetitle)
        if not is_validated:
            return

        update_vacancy = {'id': self.request_id,
                          'modifiedUserID': self.UserID,
                          'responsibleID': ','.join(map(str, self.responsible_choice_list)),
                          'fileCV': self.upload_filename,
                          'statusID': None or self.customStatusID[
                              self.status_box.current()],
                          'startWork': self._convert_date(
                              self.plannedDateStartWorkEntry.get())
                          }
        update_success = self.conn.update_vacancy(**update_vacancy)
        if update_success == 1:
            messagebox.showinfo(
                messagetitle, 'Заявка обновлена'
            )
            self._clear()
            # Если заявку возвращают в работу - удаляем согласованное ранее резюме
            if self.customStatusID[self.status_box.current()] == 2:
                self._remove_uploaded_file()
            self.controller._show_frame('PreviewForm')
        else:
            # self._remove_upload_file()
            messagebox.showerror(
                messagetitle, 'Произошла ошибка при обновлении заявки'
            )
            # МВЗ, Договор, Арендодатель, ЕГРПОУ, Описание

    def _convert_str_date(self, date):
        """ Take str and convert it into date format.
            date: str in format '%d[./]%m[./]%y' or '%d[./]%m[./]%Y'.
        """
        date_time_str = date
        date_time_obj = dt.datetime.strptime(date_time_str, '%Y-%m-%d')
        return date_time_obj.date()

    def _restraint_by_office(self, event):
        """ Shows mvz_sap that corresponds to chosen MVZ and restraint offices.
            If 1 office is available, choose it, otherwise make box active.
        """
        # tcl language has no notion of None or a null value, so use '' instead
        self.mvz_sap = self.get_mvzSAP(self.mvz_current.get()) or ''

    def _row1_pack(self):
        self.request_info_label.pack(side=tk.LEFT, anchor=tk.W)

    def _row2_pack(self):
        self.separator.pack(fill='x')

    def _row3_pack(self):
        self.responsible_label.pack(side=tk.LEFT)
        self.menubutton.pack(side=tk.RIGHT, padx=17)

    def _row4_pack(self):
        self.attach_label.pack(side=tk.LEFT, padx=0)

    def _row5_pack(self):
        self.plannedDateStartWorklabel.pack(side=tk.LEFT)
        self.plannedDateStartWorkEntry.pack(side=tk.RIGHT, padx=17)

    def _row6_pack(self):
        self.status_label.pack(side=tk.LEFT)
        self.status_box.pack(side=tk.RIGHT, padx=17)

    def _top_pack(self):
        self.main_label.pack(side=tk.TOP, expand=False, anchor=tk.NW)

    def _validate_request_creation(self, messagetitle):
        """ Check if all fields are filled properly. """
        # if not self.responsible_box.get():
        #     messagebox.showerror(
        #         messagetitle, 'Вы не загрузили резюме согласованного кандидата'
        #     )
        #     return False
        return True

class PreviewForm(RecruitingFrame):
    def __init__(self, parent, controller, connection, user_info,
                 office, responsible, status_list, **kwargs):
        super().__init__(parent, controller, connection, user_info, office)

        self.statusID, self.status_list = zip(*[(None, 'Все'), ] + status_list)
        self.responsibleID, self.responsible = zip(
            *[(None, 'Все'), ] + responsible)
        self.responsible_choice = (dict(responsible))
        self.responsible_choice_list = []
        self.UserID = self.user_info.UserID
        self.userOfficeID = self.user_info.officeID
        self.userDepartmentID = self.user_info.departmentID
        self.isHR = self.user_info.isHR
        self.isSuperHR = self.user_info.isSuperHR
        self.isAccess = self.user_info.isAccess

        # List of functions to get vacancies
        # determines what vacancies will be shown when refreshing
        self.vacancies_list = [self._get_all_vacancies]
        self.get_vacancies = self._get_all_vacancies
        # Parameters for sorting
        self.rows = None  # store all rows for sorting and redrawing
        self.sort_reversed_index = None  # reverse sorting for the last sorted column
        self.month = list(month_name)
        self.month_default = self.month[datetime.now().month]

        # Top Frame with description and user name
        top = tk.Frame(self, name='top_cf', padx=5)

        main_label = tk.Label(top, text='Просмотр заявок на персонал',
                              padx=5, font=('Calibri', 10, 'bold'))
        main_label.pack(side=tk.LEFT, expand=False, anchor=tk.NW)

        self._add_copyright(top)
        self._add_user_label(top)

        top.pack(side=tk.TOP, fill=tk.X, expand=False)

        # Filters
        filterframe = ttk.LabelFrame(self, text=' Фильтры ', name='filterframe')

        # First Filter Frame with (MVZ, office)
        row1_cf = tk.Frame(filterframe, name='row1_cf', padx=15, pady=5)

        self.office_label = tk.Label(row1_cf, text='Офис инициатора', padx=10)
        self.office_box = ttk.Combobox(row1_cf, width=40, state='readonly')
        self.office_box['values'] = list(self.office)

        self.responsible_label = tk.Label(row1_cf, text='Ответственный за заявку',
                                          padx=20)
        self.menubutton_text = tk.StringVar()
        self.menubutton = tk.Menubutton(row1_cf,
                                        textvariable=self.menubutton_text,
                                        indicatoron=True, borderwidth=1,
                                        relief="raised")
        self.menu_choice_responsible = tk.Menu(self.menubutton, tearoff=False)
        self.menubutton_text.set("Выбрать из списка")
        self.menubutton.configure(menu=self.menu_choice_responsible)
        self.choices = {}
        for choice in self.responsible_choice.values():
            self.choices[choice] = tk.IntVar(value=0)
            self.menu_choice_responsible.add_checkbutton(label=choice,
                                                         variable=self.choices[
                                                             choice],
                                                         onvalue=1, offvalue=0,
                                                         command=self._responsible_choice_list)

        self.status_label = tk.Label(row1_cf, text='Статус заявки', padx=20)
        self.status_box = ttk.Combobox(row1_cf, width=15, state='readonly')
        self.status_box['values'] = self.status_list

        self.bt3_1 = ttk.Button(row1_cf, text="Применить фильтр", width=20,
                                command=self._use_filter_and_refresh)
        self.bt3_2 = ttk.Button(row1_cf, text="Очистить фильтр", width=20,
                                command=self._clear_filters)

        # Pack row1_cf
        self._row1_pack()
        row1_cf.pack(side=tk.TOP, fill=tk.X)

        # Second Fill Frame with (Plan date, Sum, Tax)
        row2_cf = tk.Frame(filterframe, name='row2_cf', padx=15)

        # Pack row2_cf
        self._row2_pack()
        row2_cf.pack(side=tk.TOP, fill=tk.X)
        filterframe.pack(side=tk.TOP, fill=tk.X, expand=False, padx=10, pady=5)

        # Third Fill Frame (checkbox + button to apply filter)
        row3_cf = tk.Frame(filterframe, name='row3_cf', padx=15)

        # Pack row3_cf
        self._row3_pack()
        row3_cf.pack(side=tk.TOP, fill=tk.X, pady=10)

        # Set all filters to default
        self._clear_filters()

        # Text Frame
        preview_cf = ttk.LabelFrame(self, text=' Список заявок ',
                                    name='preview_cf')

        # column name and width
        self.headings = {'№ п/п': 30, 'ID': 0, 'Номер заявки': 90, 'UserID': 0,
                         'Офис': 220,
                         'Департамент': 0,
                         'Инициатор': 120, 'Дата внесения': 70,
                         'Плановая дата': 70, 'Дата выхода': 70,
                         'Должность кандидата': 120, 'ResponsibleUserID': 0,
                         'Ответственный': 100,
                         'StatusID': 0, 'Тип заявки': 50, 'Статус': 80,
                         'Комментарий': 0,'Файл заявки': 0, 'Файл резюме': 0,
                         'Кем изменено': 0
                         }

        self.table = ttk.Treeview(preview_cf, show='headings',
                                  selectmode='browse',
                                  style='HeaderStyle.Treeview'
                                  )

        self._init_table(preview_cf)
        self.table.pack(expand=tk.YES, fill=tk.BOTH)

        # asserts for headings used through script as indices
        head = self.table["columns"]
        msg = 'Heading order must be reviewed. Wrong heading: '
        assert head[1] == 'ID', '{}ID'.format(msg)
        assert head[-5] == 'Статус', '{}Статус'.format(msg)

        # Bottom Frame with buttons
        bottom_cf = tk.Frame(self, name='bottom_cf')
        # Show create buttons only for users with rights
        if self.isHR == 0 and self.isAccess == 1 or self.isSuperHR == 1:
            bt1 = ttk.Button(bottom_cf, text="Создать заявку", width=25,
                             command=lambda: controller._show_frame(
                                 'CreateForm'))
            bt1.pack(side=tk.LEFT, padx=10, pady=10)

        if self.isHR or self.UserID == 1:
            bt3 = ttk.Button(bottom_cf, text="Управление заявкой",
                             width=30, command=self._edit_current_request)
            bt3.pack(side=tk.LEFT, padx=10, pady=10)

        bt6 = ttk.Button(bottom_cf, text="Выход", width=10,
                         command=controller._quit)
        bt6.pack(side=tk.RIGHT, padx=10, pady=10)

        bt5 = ttk.Button(bottom_cf, text="Подробно", width=10,
                         command=self._show_detail)
        bt5.pack(side=tk.RIGHT, padx=10, pady=10)

        bt4 = ttk.Button(bottom_cf, text="Экспорт в Excel", width=15,
                         command=self._export_to_excel)
        bt4.pack(side=tk.RIGHT, padx=10, pady=10)

        # Pack frames
        bottom_cf.pack(side=tk.BOTTOM, fill=tk.X, expand=False)
        preview_cf.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=10, pady=5)

    def _responsible_choice_list(self):
        self.responsible_choice_list = []
        for name, var in self.choices.items():
            if var.get() == 1:
                self.responsible_choice_list.append(self.getUserID(name))

    # Deselect checked row in menu (destroy and create menubutton again)
    def _deselect_checked_responsible(self):
        self.responsible_choice_list.clear()
        self.menu_choice_responsible.destroy()
        self.menu_choice_responsible = tk.Menu(self.menubutton, tearoff=False)
        self.menubutton.configure(menu=self.menu_choice_responsible)
        for choice in self.responsible_choice.values():
            self.choices[choice] = tk.IntVar(value=0)
            self.menu_choice_responsible.add_checkbutton(label=choice,
                                                 variable=self.choices[choice],
                                                 onvalue=1, offvalue=0,
                                                 command=self._responsible_choice_list)

    def getUserID(self, items):
        UserID = int
        for k,v in self.responsible_choice.items():
            if v == items:
                UserID = k
        return UserID

    def _add_copyright(self, parent):
        """ Adds user name in the top right corner. """
        copyright_label = tk.Label(parent, text="О программе",
                                   font=('Arial', 8, 'underline'),
                                   cursor="hand2")
        copyright_label.bind("<Button-1>", self._show_about)
        copyright_label.pack(side=tk.RIGHT, anchor=tk.N)

    def _center_popup_window(self, newlevel, w, h, static_geometry=True):
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()

        start_x = int((screen_width / 2) - (w / 2))
        start_y = int((screen_height / 2) - (h * 0.7))

        if static_geometry == True:
            newlevel.geometry('{}x{}+{}+{}'.format(w, h, start_x, start_y))
        else:
            newlevel.geometry('+{}+{}'.format(start_x, start_y))

    def _change_preview_state(self):
        """ Change payments state that determines which payments will be shown.
        """
        self.get_vacancies = self._get_all_vacancies

    def _clear_filters(self):
        self.office_box.set('Все')
        self._deselect_checked_responsible()
        self.status_box.set('Все')

    def _edit_current_request(self):
        """ Raises UpdateForm with partially filled labels/entries. """
        curRow = self.table.focus()

        if curRow:
            # extract info to be putted in CreateForm
            to_fill = dict(zip(self.table["columns"],
                               self.table.item(curRow).get('values')))
            request_status = to_fill['Статус']
            if request_status == 'Отменена':
                messagebox.showinfo(
                    'Изменение заявки',
                    'Завка отменена и изменение в ней данных невозможно.'
                )
                return
            if request_status == 'Верифицировано':
                messagebox.showinfo(
                    'Изменение заявки',
                    'Заявка уже верифицирована и изменение в ней данных невозможно.'
                )
                return
            self.controller._fill_UpdateForm(**to_fill)
            self.controller._show_frame('UpdateForm')

    def _delete_current(self):
        """ Raises CreateForm with partially filled labels/entries. """
        curRow = self.table.focus()

        if curRow:
            # extract info to be putted in CreateForm
            to_fill = dict(zip(self.table["columns"],
                               self.table.item(curRow).get('values')))

    def _export_to_excel(self):
        if not self.rows:
            return
        headings = {k: v for k, v in self.headings.items() if k != '№ п/п'}
        isExported = export_to_excel(headings, self.rows)
        if isExported:
            messagebox.showinfo(
                'Экспорт в Excel',
                'Данные экспортированы на рабочий стол'
            )
        else:
            messagebox.showerror(
                'Экспорт в Excel',
                'При экспорте произошла непредвиденная ошибка'
            )

    def _get_all_vacancies(self):
        """ Extract information from filters and get payments list. """
        filters = {
            'statusID': (self.statusID[self.status_box.current()]),
            'officeID': (self.officeID[self.office_box.current()]),
            'responsibleID': ','.join(map(str, self.responsible_choice_list)),
            'userOfficeID': self.userOfficeID,
            'userDepartmentID': self.userDepartmentID,
            'isHR': 1 if self.isHR else 0,
            'UserID': self.UserID
        }
        self.rows = self.conn.get_vacancies_list(**filters)

    def _init_table(self, parent):
        """ Creates treeview. """
        if isinstance(self.headings, dict):
            self.table["columns"] = tuple(self.headings.keys())
            self.table["displaycolumns"] = tuple(k for k in self.headings.keys()
                                                 if
                                                 k not in ('ID', 'Департамент',
                                                           'UserID',
                                                           'ResponsibleUserID',
                                                           'StatusID',
                                                           'Комментарий',
                                                           'Файл заявки',
                                                           'Файл резюме',
                                                           'Кем изменено'))

            for head, width in self.headings.items():
                self.table.heading(head, text=head, anchor=tk.CENTER)
                if head in ('Офис', 'Инициатор', 'Должность кандидата', 'Ответственный', 'Статус'):
                    self.table.column(head, width=width, anchor="w")
                else:
                    self.table.column(head, width=width, anchor=tk.CENTER)

        else:
            self.table["columns"] = self.headings
            self.table["displaycolumns"] = self.headings
            for head in self.headings:
                self.table.heading(head, text=head, anchor=tk.CENTER)
                self.table.column(head, width=50 * len(head), anchor=tk.CENTER)

        for tag, bg, color in zip(self.status_list[1:6], (
                '#ff867c', '#fff9c4', '#ffb6b9', '#80e27e', '#eae3e3'), (
                '#000000', '#000000', '#000000', '#000000', '#555')):
            self.table.tag_configure(tag, background=bg, foreground=color)

        self.table.bind('<Double-1>', self._show_detail)
        self.table.bind('<Button-1>', self._sort, True)

        scrolltable = tk.Scrollbar(parent, command=self.table.yview)
        self.table.configure(yscrollcommand=scrolltable.set)
        scrolltable.pack(side=tk.RIGHT, fill=tk.Y)

    def _raise_Toplevel(self, frame, title, width, height,
                        static_geometry=True, options=()):
        """ Create and raise new frame.
        Input:
        frame - class, Frame class to be drawn in Toplevel;
        title - str, window title;
        width - int, width parameter to center window;
        height - int, height parameter to center window;
        static_geometry - bool, if True - width and height will determine size
            of window, otherwise size will be determined dynamically;
        options - tuple, arguments that will be sent to frame.
        """
        newlevel = tk.Toplevel(self.parent)
        # newlevel.transient(self)  # disable minimize/maximize buttons
        newlevel.title(title)
        newlevel.bind('<Escape>', lambda e, w=newlevel: w.destroy())
        frame(newlevel, *options)
        newlevel.resizable(width=False, height=False)
        self._center_popup_window(newlevel, width, height, static_geometry)
        newlevel.focus()
        newlevel.grab_set()

    def _refresh(self):
        """ Refresh information about vacancies. """
        try:
            self.get_vacancies()
        except MonthFilterError as e:
            messagebox.showerror(self.controller.title(), e.message)
            return
        self._show_rows(self.rows)

    def _resize_columns(self):
        """ Resize columns in treeview. """
        self.table.column('#0', width=36)
        for head, width in self.headings.items():
            self.table.column(head, width=width)

    def _row1_pack(self):
        self.office_label.pack(side=tk.LEFT)
        self.office_box.pack(side=tk.LEFT, padx=5, pady=5)
        self.responsible_label.pack(side=tk.LEFT)
        self.menubutton.pack(side=tk.LEFT, padx=5, pady=5)
        self.status_label.pack(side=tk.LEFT)
        self.status_box.pack(side=tk.LEFT, padx=5, pady=5)

        self.bt3_2.pack(side=tk.RIGHT, padx=10)
        self.bt3_1.pack(side=tk.RIGHT, padx=10)

    def _row2_pack(self):
        pass

    def _row3_pack(self):
        pass

    def _show_about(self, event=None):
        """ Raise frame with info about app. """
        self._raise_Toplevel(frame=AboutFrame,
                             title='Заявки на поиск персонала ',
                             width=400, height=150)

    def _show_detail(self, event=None):
        """ Show details when double-clicked on row. """
        show_detail = (not event or (self.table.identify_row(event.y) and
                                     int(self.table.identify_column(event.x)[
                                         1:]) > 0
                                     )
                       )
        if show_detail:
            curRow = self.table.focus()
            if curRow:
                newlevel = tk.Toplevel(self.parent)
                newlevel.withdraw()
                # newlevel.transient(self.parent)  # disable minimize/maximize buttons
                newlevel.title('Просмотр заявки на персонал')
                newlevel.iconbitmap(os.path.join(sys.path[0],'resources/file.ico'))
                newlevel.bind('<Escape>', lambda e, w=newlevel: w.destroy())
                DetailedPreview(newlevel, self, self.conn, self.userID,
                                self.headings,
                                self.table.item(curRow).get('values'),
                                self.table.item(curRow).get('tags'))
                newlevel.resizable(width=False, height=False)
                # width is set implicitly in DetailedPreview._newRow
                # based on columnWidths values
                self._center_popup_window(newlevel, 700, 300,
                                          static_geometry=False)
                newlevel.deiconify()
                newlevel.focus()
                newlevel.grab_set()
        else:
            # if double click on header - redirect to sorting rows
            self._sort(event)

    def _sort(self, event):
        if self.table.identify_region(event.x,
                                      event.y) == 'heading' and self.rows:
            # determine index of displayed column
            disp_col = int(self.table.identify_column(event.x)[1:]) - 1
            if disp_col < 1:  # ignore sort by '№ п/п' and checkboxes
                return
            # determine index of this column in self.rows
            # substract 1 because of added '№ п/п' which don't exist in data
            sort_col = self.table["columns"].index(
                self.table["displaycolumns"][disp_col]) - 1
            self.rows.sort(key=lambda x: x[sort_col],
                           reverse=self.sort_reversed_index == sort_col)
            # store index of last sorted column if sort wasn't reversed
            self.sort_reversed_index = None if self.sort_reversed_index == sort_col else sort_col
            self._show_rows(self.rows)

    def _show_rows(self, rows):
        """ Refresh table with new rows. """
        self.table.delete(*self.table.get_children())

        if not rows:
            return
        for i, row in enumerate(rows):
            self.table.insert('', tk.END,
                              values=(i + 1,) + tuple(
                                  map(lambda val: self._format_float(val)
                                  if isinstance(val, Decimal) else val, row)),
                              tags=(row[-5], 'unchecked'))

    def _use_filter_and_refresh(self):
        """ Change state to filter usage. """
        self._change_preview_state()
        self._refresh()

class DetailedPreview(tk.Frame):
    """ Class that creates Frame with information about chosen request. """

    def __init__(self, parent, parentform, conn, userID, head, info, tags):
        super().__init__(parent)
        self.parent = parent
        self.parentform = parentform
        self.conn = conn
        self.userID = userID
        self.rowtags = tags
        self.initiatorID = info[3]
        self.ID = info[1]
        self.statusID = info[13]
        self.filename_preview = str()
        self.cv_preview = str()
        # Top Frame with description and user name
        self.top = tk.Frame(self, name='top_cf', padx=5, pady=5)

        # Create a frame on the canvas to contain the buttons.
        self.table_frame = tk.Frame(self.top)

        # Add info to table_frame
        fonts = (('Arial', 9, 'bold'), ('Arial', 10))
        for row in zip(range(len(head)), zip(head, info)):
            if row[1][0] not in ('№ п/п', 'UserID', 'ID',
                                 'ResponsibleUserID', 'StatusID', 'Ответственный'):
                if row[1][0] == 'Файл заявки' and (row[1][1] != '-'
                                                   or row[1][1] is not None):
                    self.filename_preview = row[1][1]
                if row[1][0] == 'Файл резюме' and (row[1][1] != '-'
                                                   or row[1][1] is not None):
                    self.cv_preview = row[1][1]
                self._newRow(self.table_frame, fonts, *row)

        self.appr_label = tk.Label(self.top, text='Ответственные за заявку',
                                   padx=10, pady=5, font=('Arial', 10, 'bold'))

        # Top Frame with list mvz
        self.appr_cf = tk.Frame(self, name='appr_cf', padx=5)
        #
        # Add list of all mvz for current contract
        fonts = (('Arial', 10), ('Arial', 10))
        self.current_responsible = self.conn.get_current_responsible(self.ID)
        for rowNumber, row in enumerate(self.current_responsible):
            self._newRow(self.appr_cf, fonts, rowNumber + 1, row)

        self._add_buttons()
        self._pack_frames()

    def _open_file(self):
        try:
            pathToFile = UPLOAD_PATH + "\\" + self.filename_preview
            return os.startfile(pathToFile)
        except FileNotFoundError:
            FileNotFound()

    def _open_cv(self):
        try:
            pathToFile = UPLOAD_PATH + "\\" + self.cv_preview
            return os.startfile(pathToFile)
        except FileNotFoundError:
            FileNotFound()

    def _add_buttons(self):
        # Bottom Frame with buttons
        self.bottom = tk.Frame(self, name='bottom')
        if self.filename_preview:
            bt1 = ttk.Button(self.bottom, text="Требования", width=15,
                             command=self._open_file)
            bt1.pack(side=tk.LEFT, padx=5, pady=5)
        if self.cv_preview and self.cv_preview != '-':
            bt4 = ttk.Button(self.bottom, text="Резюме", width=15,
                             command=self._open_cv)
            bt4.pack(side=tk.LEFT, padx=5, pady=5)
        bt2 = ttk.Button(self.bottom, text="Закрыть", width=10,
                         command=self.parent.destroy)
        bt2.pack(side=tk.RIGHT, padx=5, pady=0)

        # show cancel button for initiator users
        if (self.userID == self.initiatorID or self.userID in (1,4)) and self.statusID not in (4, 5) :
            bt3 = ttk.Button(self.bottom, text="Отменить заявку", width=18,
                             command=self._delete, style='ButtonRed.TButton')
            bt3.pack(side=tk.RIGHT, padx=5, pady=0)

    def _delete(self):
        mboxname = 'Отмена заявки на поиск персонала'
        confirmed = messagebox.askyesno(title=mboxname,
                                        message='Вы уверены, что хотите отменить эту заявку?')
        if confirmed:
            self.conn.update_vacancy(self.ID, self.userID, None, None, 5, None)
            messagebox.showinfo(mboxname, 'Заявка отменена')
            self.parentform._refresh()
            self.parent.destroy()

    def _newRow(self, frame, fonts, rowNumber, info):
        """ Adds a new line to the table. """

        numberOfLines = []  # List to store number of lines needed
        columnWidths = [25, 60]  # Width of the different columns in the table

        # Find the length and the number of lines of each element and column
        for index, item in enumerate(info):
            # minimum 1 line + number of new lines + lines that too long
            numberOfLines.append(1 + str(item).count('\n') +
                                 sum(floor(len(s) / columnWidths[index]) for s
                                     in str(item).split('\n'))
                                 )

        # Find the maximum number of lines needed
        lineNumber = max(numberOfLines)

        # Define labels (columns) for row
        def form_column(rowNumber, lineNumber, col_num, cell, fonts):
            col = tk.Text(frame, bg='white', padx=10)
            col.insert(1.0, cell)
            col.grid(row=rowNumber, column=col_num + 1, sticky='news')
            col.configure(width=columnWidths[col_num],
                          height=min(9, lineNumber),
                          font=fonts[col_num], state="disabled")
            if lineNumber > 9 and col_num == 1:
                scrollbar = tk.Scrollbar(frame, command=col.yview)
                scrollbar.grid(row=rowNumber, column=col_num + 2, sticky='nsew')
                col['yscrollcommand'] = scrollbar.set

        for col_num, cell in enumerate(info):
            form_column(rowNumber, lineNumber, col_num, cell, fonts)

    def _pack_frames(self):
        self.top.pack(side=tk.TOP, fill=tk.X, expand=False)
        self.bottom.pack(side=tk.BOTTOM, fill=tk.X, expand=False)
        self.appr_cf.pack(side=tk.TOP, fill=tk.X)
        self.table_frame.pack()
        if self.current_responsible:
            self.appr_label.pack(side=tk.LEFT, expand=False)
        self.pack()

class AboutFrame(tk.Frame):
    """ Creates a frame with copyright and info about app. """

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

        self.top = ttk.LabelFrame(self, name='top_af')

        logo = tk.PhotoImage(file='resources/file.png')
        self.logo_label = tk.Label(self.top, image=logo)
        self.logo_label.image = logo  # keep a reference to avoid gc!

        self.copyright_text = tk.Text(self.top, bg='#f1f1f1',
                                      font=('Arial', 8), relief=tk.FLAT)
        hyperlink = HyperlinkManager(self.copyright_text)

        def link_instruction():
            path = 'resources\\README.pdf'
            os.startfile(path)

        self.copyright_text.insert(tk.INSERT,
                                   'Платформа для создания заявок на поиск  \n'
                                   'персонала (версия ' + __version__ + ')\n')
        self.copyright_text.insert(tk.INSERT, "\n")

        def link_license():
            path = 'resources\\LICENSE.txt'
            os.startfile(path)

        self.copyright_text.insert(tk.INSERT,
                                   'Copyright © 2022 Офис прогнозирования\n'
                                   'Департамент мастер-данных и отчетности\n')
        self.copyright_text.insert(tk.INSERT, 'MIT License',
                                   hyperlink.add(link_license))

        self.bt = ttk.Button(self, text="Закрыть", width=10,
                             command=parent.destroy)
        self.pack_all()

    def pack_all(self):
        self.bt.pack(side=tk.BOTTOM, pady=5)
        self.top.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=10)
        self.logo_label.pack(side=tk.LEFT, padx=10)
        self.copyright_text.pack(side=tk.LEFT, padx=10)
        self.pack(fill=tk.BOTH, expand=True)

if __name__ == '__main__':
    from db_connect import DBConnect
    from collections import namedtuple

    UserInfo = namedtuple('UserInfo', ['UserID', 'ShortUserName',
                                       'AccessType', 'isSuperUser', 'GroupID',
                                       'PayConditionsID'])

    with DBConnect(server='s-kv-center-s59', db='AnalyticReports') as sql:
        try:
            app = RecruitingApp(connection=sql,
                                user_info=UserInfo(24, 'TestName', 2, 1, 1, 2),
                                office=[('20511RC191', '20511RC191', 'Офис'),
                                        ('40900A2595', '40900A2595', 'Офис')],
                                status_list=[(1, 'На согл.'), (2, 'Отозв.')]
                                )
            app.mainloop()
        except Exception as e:
            print(e)
            raise
    input('Press Enter...')


from dash import dash_table, html
import dash_bootstrap_components as dbc
from dash_iconify import DashIconify
import dash_mantine_components as dmc

def multiselect(select_options, default_value, select_label, select_id):
    return dmc.MultiSelect(
        data=select_options,
        value=default_value,
        label=select_label,
        icon=DashIconify(icon='flat-color-icons:line-chart'),
        searchable=True,
        nothingFound="Dataset not found",
        className='general-multiselect',
        id=select_id
    )

def year(input_label, default_value, input_id, min_year=1940, max_year=2023):
    return dmc.NumberInput(
        label=input_label,
        value=default_value,
        min=min_year,
        max=max_year,
        step=1,
        icon=DashIconify(icon='clarity:date-line'),
        className='general-number-input',
        id=input_id
    )

def button(button_label, button_id, icon=None):
    return dmc.Button(
        button_label,
        leftIcon=icon,
        variant="gradient",
        gradient={'from': 'grape', 'to': 'gray', 'deg': 45},
        className='general-button',
        id=button_id
    )

def checkbox(checkbox_label, checkbox_id):
    return dmc.Checkbox(
        label=checkbox_label,
        checked=False,
        className='general-checkbox',
        id=checkbox_id
    )

def select(select_options, default_value, select_label, select_id):
    return dmc.Select(
        data=select_options,
        value=default_value,
        label=select_label,
        searchable=True,
        nothingFound="Option not found",
        className='general-select',
        id=select_id
    )

def accordion_header(displayed_text):
    return dmc.Badge(
        displayed_text,
        variant='gradient',
        gradient={'from': 'rgb(192, 135, 192)', 'to': 'rgb(106, 79, 101)'},
        opacity=0.99,
        size='lg',
        radius='md',
        style={'width': '100%'}
    )

def request_data_header(text):
    return dmc.Text(
        text,
        size=18,
        weight=600,
        align='center'
    )

def request_data_column(header_text, year_inputs, button, *multiselects):
    return dbc.Col(
        dbc.Stack(
            [
                dmc.Text(header_text, size=18, weight=600, align='center'),
                *multiselects,
                dbc.Stack(
                    year_inputs,
                    direction='horizontal',
                    gap=3
                ),
                button
            ],
            gap=3
        )
    )

def icon(icon_name):
    return DashIconify(
        icon=icon_name,
        color="white",
        width=24
    )

def datatable(header, total_null_values, null_columns_string, df):
    return [
        dmc.Text(header, weight=550, size='lg', style={'margin-top': '10px', 'margin-bottom': '10px'}),
        dmc.Text(total_null_values, weight=410, size='sm', className='general-text'),
        dmc.Text(null_columns_string, weight=410, size='sm', className='general-text'),
        html.Div(
            dash_table.DataTable(
                data=df.to_dict('records'),
                columns=[{'name': str(i), 'id': str(i)} for i in df.columns],
                fill_width=False,
                cell_selectable=False,
                style_as_list_view=True,
                style_header={
                    'color': 'rgba(220, 220, 220, 0.95)',
                    'padding': '10px',
                    'fontFamily': 'Arial, sans-serif',
                    'fontSize': '14px',
                    'fontWeight': 'bold'
                },
                style_data={'color': 'rgba(220, 220, 220, 0.85)'},
                style_cell={'fontFamily': 'Arial, sans-serif', 'fontSize': '14px'},
                style_cell_conditional=[{'textAlign': 'center'}],
                id='monthly_dash_table'
            )
        )
    ]


# StyleGAN2 - https://arxiv.org/abs/1912.04958
# Not implemented features
# - Path length regularization
#
# Code reference:
# https://github.com/NVlabs/stylegan2

import math
from dataclasses import dataclass, replace
from typing import Optional

import torch
import torch.nn.functional as F
from torch import Tensor, nn

from .nvidia_ops import Blur
from .progressive_gan import ProgressiveGANDiscriminator, init_weights
from .stylegan import MappingNetwork, StyleGANConfig

batched_conv2d = torch.vmap(F.conv2d)

@dataclass
class StyleGAN2Config(StyleGANConfig):
    blur_size: int = 4  # override StyleGAN
    residual_D: bool = True

class StyleGAN2Discriminator(ProgressiveGANDiscriminator):
    def __init__(self, config: Optional[StyleGAN2Config] = None, **kwargs):
        config = config or StyleGAN2Config()
        config = replace(config, **kwargs)
        super().__init__(config)

class ModulatedConv2d(nn.Module):
    def __init__(self, in_dim: int, out_dim: int, kernel_size: int, w_dim: int, demodulation: bool = True):
        super().__init__()
        # to contain weights for initialization and parametrization
        self.conv = nn.Conv2d(in_dim, out_dim, kernel_size, padding=(kernel_size - 1) // 2)
        self.style_weight = nn.Linear(w_dim, in_dim)  # A in paper
        self.demodulation = demodulation

    def forward(self, imgs: Tensor, w_embs: Tensor):
        b, c, _, _ = imgs.shape

        # modulation
        style = self.style_weight(w_embs).view(b, 1, c, 1, 1) + 1
        weight = self.conv.weight.unsqueeze(0) * style
        if self.demodulation:
            weight = weight / torch.linalg.vector_norm(weight, dim=(2, 3, 4), keepdim=True)

        bias = self.conv.bias.view(1, -1).expand(b, -1) if self.conv.bias is not None else None
        imgs = batched_conv2d(imgs, weight, bias, padding=self.conv.padding)
        return imgs

class StyleGAN2GeneratorBlock(nn.Module):
    def __init__(self, in_dim: int, out_dim: int, config: StyleGAN2Config, upsample: bool = False):
        super().__init__()
        # TODO: merge upsample with conv into conv_transpose (up_conv_blur)
        self.up = nn.Upsample(scale_factor=2.0) if upsample else nn.Identity()
        self.conv = ModulatedConv2d(in_dim, out_dim, 3, config.w_dim)
        self.blur = Blur(config.blur_size) if upsample else nn.Identity()
        self.noise_weight = nn.Parameter(torch.tensor(0.0))  # B in paper
        self.act = config.act()

    def forward(self, imgs: Tensor, w_embs: Tensor):
        imgs = self.blur(self.conv(self.up(imgs), w_embs))
        b, _, h, w = imgs.shape
        noise = torch.randn(b, 1, h, w, device=imgs.device)
        return self.act(imgs + noise * self.noise_weight)

class RGBLayer(ModulatedConv2d):
    def __init__(self, in_dim: int, config: StyleGAN2Config):
        super().__init__(in_dim, config.img_channels, 1, config.w_dim, demodulation=False)

class StyleGAN2Generator(nn.Module):
    def __init__(self, config: Optional[StyleGAN2Config] = None, **kwargs):
        config = config or StyleGAN2Config()
        config = replace(config, **kwargs)
        assert config.img_size > 4 and math.log2(config.img_size).is_integer()
        super().__init__()
        self.mapping_network = MappingNetwork(config)

        map_size = config.init_map_size
        in_depth = config.input_depth
        self.learned_input = nn.Parameter(torch.empty(1, in_depth, map_size, map_size))
        self.up_blur = Blur(config.blur_size, up=2)

        depth = config.min_channels * config.img_size // map_size
        out_depth = min(depth, config.max_channels)

        self.first_block = StyleGAN2GeneratorBlock(in_depth, out_depth, config)
        self.first_to_rgb = RGBLayer(out_depth, config)
        in_depth = out_depth
        depth //= 2

        self.stages = nn.ModuleList()
        while map_size < config.img_size:
            out_depth = min(depth, config.max_channels)
            stage = dict(
                block1=StyleGAN2GeneratorBlock(in_depth, out_depth, config, upsample=True),
                block2=StyleGAN2GeneratorBlock(out_depth, out_depth, config),
                to_rgb=RGBLayer(out_depth, config),
            )
            self.stages.append(nn.ModuleDict(stage))
            in_depth = out_depth
            depth //= 2
            map_size *= 2

        self.reset_parameters()

    def reset_parameters(self):
        nn.init.ones_(self.learned_input)
        self.apply(init_weights)

    def forward(self, z_embs: Tensor):
        w_embs = self.mapping_network(z_embs)

        x = self.learned_input.expand(z_embs.size(0), -1, -1, -1)
        x = self.first_block(x, w_embs[:, 0])
        y = self.first_to_rgb(x, w_embs[:, 1])

        for i, stage in enumerate(self.stages):
            x = stage["block1"](x, w_embs[:, (i + 1) * 2])
            x = stage["block2"](x, w_embs[:, (i + 1) * 2 + 1])
            y = stage["to_rgb"](x, w_embs[:, (i + 1) * 2 + 1]) + self.up_blur(y)

        return y


from keras.models import Sequential
from keras.layers import Dense, Conv2D
from keras.layers import MaxPool2D, Flatten
from keras import optimizers, losses

from keras.models import Model
from keras.models import Input

import keras

import sys
from load_mnist_data import load_mnist_data_vectors

def MLP(input_shape, num_classes):
    '''
    model = Sequential([
        Dense(64, activation='relu', input_shape=input_shape),
        Dense(32, activation='relu'),
        Dense(num_classes, activation='softmax')
    ])
    '''

    inputs = Input(shape=input_shape)
    x = Dense(64, activation='relu')(inputs)
    x = Dense(32, activation='relu')(x)
    outputs = Dense(num_classes, activation='softmax')(x)
    model = Model(inputs=inputs, outputs=outputs)

    return model

if __name__ == '__main__':
    (x_train, y_train), (x_test, y_test) = load_mnist_data_vectors()

    epochs = 3
    batch_size = 128

    input_shape = (784,)
    num_classes = 10

    model = MLP(input_shape, num_classes)

    model.compile(loss=losses.categorical_crossentropy,
                  optimizer=optimizers.SGD(lr=0.01),
                  metrics=['accuracy'])

    tensorboard = keras.callbacks.TensorBoard(log_dir='./logs_mlp')
    checkpointer = keras.callbacks.ModelCheckpoint(filepath='./ckpt_mlp/weight.{epoch:02d}-{val_loss:.2f}.h5',
                                                   monitor='val_loss')

    history = model.fit(x_train, y_train,
                        batch_size=batch_size,
                        epochs=epochs,
                        verbose=1,
                        callbacks=[tensorboard, checkpointer],
                        validation_data=(x_test, y_test))

    model.save('model/mnist_mlp.h5')

    del model
    model = keras.models.load_model('model/mnist_mlp.h5')

    score = model.evaluate(x_test, y_test, verbose=0)
    print('Test loss:', score[0])
    print('Test accuracy:', score[1])


##################################################
# VOICEVOX　GPUメモリ監視プログラム
##################################################
import os
import win32service
import win32serviceutil
import win32event
import logging
import servicemanager
import socket
import datetime
import time
import subprocess
from subprocess import PIPE
import time
from io import StringIO
import pandas as pd
import psutil

##################################################
# ログ設定
##################################################
today = datetime.date.today()
logging.basicConfig(
    # ログ出力先　※環境によって変更する
    filename = 'C:\\voicevox_engine\\voicevox-gpu-memory-monitor\\log\\{}-app.log'.format(today.strftime('%Y%m%d')),
    level = logging.DEBUG,
    format="%(asctime)s:LINE[%(lineno)s] %(levelname)s %(message)s"
)

##################################################
# PythonServiceクラス
##################################################
class PythonService(win32serviceutil.ServiceFramework):
    # サービス名
    _svc_name_ = 'VoiceVox Engine Service'
    # 表示名(サービス画面にはこれを表示)
    _svc_display_name_ = 'VoiceVox Engine Service'
    # サービスの説明
    _svc_description_ = '一定周期でVoiceVox Engineで使っているGPUメモリを監視する'
    # 実行コマンド ※環境によって変更する
    _exe_path = "C:\\voicevox_engine\\0.14.6\\windows-directml\\run.exe --use_gpu --host 192.168.50.222"
    # メモリ仕使用率閾値　※環境によって変更する
    _threshold = 90
    # インターバル(秒)　※環境によって変更する
    _interval = 10.0
    # VOICEVOX EXE名
    _exe_name = "run.exe"
    # CSV に出力するカラム一覧
    _keys = ["timestamp","memory.total","memory.free","memory.used"]
    # GPUスペック出力カラム一覧
    _spec_keys = ["index","name","memory.total"]

    # 初期化
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self,args)
        self.stop_event = win32event.CreateEvent(None,0,0,None)
        socket.setdefaulttimeout(60)
        self.stop_requested = False

    # サービス停止
    def SvcStop(self):
        logging.info("execute service stop")
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.stop_event)
        self.stop_requested = True

    # サービス開始
    def SvcDoRun(self):
        logging.info("execute service start")
        servicemanager.LogMsg(
			servicemanager.EVENTLOG_INFORMATION_TYPE,
			servicemanager.PYS_SERVICE_STARTED,
			(self._svc_name_,'')
			)
        # メイン処理スタート
        self.main_loop()

    # GPUスペック出力関数
    def get_gpu_specs(self):
        output = self.get_gpu_info(self._spec_keys)
        output = pd.read_csv(StringIO(output), names=self._spec_keys)
        return output

    # GPU情報出力関数
    def get_gpu_info(self,keys=_keys, no_units=True):
        queries = ",".join(keys)
        output_fmt = "csv,noheader"
        if no_units:
            output_fmt += ",nounits"

        cmd = f"nvidia-smi --query-gpu={queries} --format={output_fmt}"
        output = subprocess.check_output(cmd, shell=True).decode()
        # カンマの後の空白を削除する
        output = output.replace(", ", ",")
        return output

    # メイン処理
    def main_loop(self):

        logging.info("service start successful")

        # GPU の一覧を取得
        gpu_specs = self.get_gpu_specs()

        # 初期画面表示
        logging.info("-----------------------------------------------------------------------------")
        logging.info("VOICEVOX GPU memory monitoring service")
        logging.info("--description--")
        logging.info(" GPU memory use rate" + str(self._threshold) + "%over VOICEVOX run.exe restart")
        logging.info(" GPU memory use rate" + str(self._interval) + "sec intarvel monitoring")
        logging.info("-----------------------------------------------------------------------------")
        logging.info("--GPU info--")
        logging.info(gpu_specs.head(1))
        logging.info("-----------------------------------------------------------------------------")
        logging.info("--exe path--")
        logging.info(self._exe_path)
        logging.info("-----------------------------------------------------------------------------")

        while True:
            if self.stop_requested:
                logging.info("service stopping")
                break
            try:
                time.sleep(self._interval)
                # logging.info('service in progress')
                #プロセス存在フラグ初期化
                process_exist_flg = False
                #使用率初期化
                use_rate = 0
                # GPU の情報を取得
                output = self.get_gpu_info(self._keys)
                # データを分割＆改行削除
                gpu_info = output.replace('\r\n','').split(',')

                # プロセス取得
                for proc in psutil.process_iter():
                    try:
                        # プロセス情報取得
                        processName = proc.name()
                        processID = proc.pid
                        cmdLine = proc.cmdline()
                        cmd_line_str = ' '.join(cmdLine)

                        # プロセス名チェック
                        if(processName == self._exe_name):
                            # プロセスあればフラグTrrue
                            process_exist_flg = True
                            # メモリ使用率計算
                            # memory.used / memory.total * 100
                            use_rate = round(int(gpu_info[3]) / int(gpu_info[1]),2) * 100
                            # 画面表示用
                            logging.info(gpu_info[0] +
                                " - " + str(gpu_info[1]) + "MB" +
                                " - " + str(gpu_info[2]) + "MB" +
                                " - " + str(gpu_info[3]) + "MB" +
                                " - " + str(use_rate) + "%" +
                                " - " + str(processName) +
                                " -  " + str(processID) +
                                " -  " + cmd_line_str)

                            #もし、使用率が閾値を超えたらプロセス再起動
                            if(use_rate > self._threshold):
                                # プロセスIDからプロセス取得
                                p = psutil.Process(processID)

                                #子のterminate
                                pid_list=[pc.pid for pc in p.children(recursive=True)]
                                for pid in pid_list:
                                    psutil.Process(pid).terminate ()
                                    logging.info("terminate child process {}" .format(pid))

                                #親のterminate
                                p.terminate ()
                                logging.info("terminate parent process {}" .format(processID))

                                #再起動
                                logging.info("voicevox process memory overflow ---> restart " + self._exe_path)
                                res_vve = subprocess.Popen(self._exe_path)

                    except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                        pass

                #プロセスが存在しなかったら起動する
                if(process_exist_flg == False):
                    logging.info("voicevox process  not found ---> start " + self._exe_path)
                    res_vve = subprocess.Popen(self._exe_path)

            except Exception as e:
                logging.error("Error occured.")
                logging.error(str(type(e)))
                logging.error(str(e.args))
                logging.error(str(e))

        logging.info("service stop successful")
        return

if __name__ == '__main__':
	win32serviceutil.HandleCommandLine(PythonService)


#simple script to start a long script
#and let it run for a set amount of time before
#stopping and restarting a new run of the script
#if the long script crashes, this script restarts
#a new run after 5 seconds

import subprocess as sp
import time

length = 60 #wait 5 seconds
duration = 300
startTime = time.time()
finalEndTime = startTime+duration  #run for a total of 20 seconds
i = 1
while(time.time() < finalEndTime):
	longScript = sp.call(['python', 'longscript.py', str(i), str(length)]) #runs longscript.py
	runTimeLen = time.time()-startTime
	print('Finished round '+str(i)+'; Runtime: '+str(round(runTimeLen/3600.0, 4))+' hours ('+str(round(runTimeLen/60.0, 4))+' minutes)')
	startTime = time.time()
	i = i+1

print('Done!')


import pandas as pd

def preprocesar_archivo_situacion_laboral(file: str, directory: str):

    filepath = directory + file

    raw_df = pd.read_excel(
        filepath,
        header=6,
        skiprows=lambda x: x in range(7, 29) or x in range(
            30, 37) or x in range(52, 59) or x in [38, 45, 60, 67],
        usecols='A, C:E',
        skipfooter=9,
        names=['area_estudio', 'trabajando', 'en desempleo', 'inactivo']
    )

    raw_df['area_estudio'] = raw_df['area_estudio'].str.strip()

    men_df = raw_df.iloc[0:13, :].copy()
    men_df['sexo'] = 'Masculino'
    men_public_df = men_df.iloc[:7, :].dropna()
    men_public_df['tipo_universidad'] = 'Pública'
    men_private_df = men_df.iloc[7:, :].dropna()
    men_private_df['tipo_universidad'] = 'Privada'

    women_df = raw_df.iloc[13:, :].copy()
    women_df['sexo'] = 'Femenino'
    women_public_df = women_df.iloc[:7, :].dropna()
    women_public_df['tipo_universidad'] = 'Pública'
    women_private_df = women_df.iloc[7:, :].dropna()
    women_private_df['tipo_universidad'] = 'Privada'

    new_df = pd.concat([men_public_df, men_private_df,women_public_df, women_private_df], ignore_index=True)

    new_df = pd.melt(
        new_df,
        id_vars=['tipo_universidad', 'area_estudio', 'sexo'],
        value_vars=['trabajando', 'en desempleo', 'inactivo'],
        var_name='situacion_laboral',
        value_name='cantidad'
    )

    new_df['año'] = 2014
    new_df['pais'] = 'Spain'

    new_df[[
        'año', 'pais', 'tipo_universidad',	'area_estudio',	'sexo',	'situacion_laboral',	'cantidad'
    ]].to_csv('/tmp/data/processed/03003.csv', index=False)

def cargar_archivo_situacion_laboral():
    pass


# Author Jacob Hoffer
# EENG427 HW02

import numpy as np

def main():

    # 2.12
    n_s = 100
    s_n = np.ones(n_s)
    u_s = 1
    p_s = (1/n_s) * np.sum(s_n - u_s)**2
    print(p_s)

    return

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Sun May 13 17:01:11 2018

@author: Administrator
"""

import PSSMmaker

#PSSMMaker.command_pssm('E:\Blast\db\Cytoplasm_test.fasta','try_once.txt','pssm1.pssm')
proseq=r'D:\pythonPSSM\Fan1105'
outdir=r'D:\pythonPSSM\456'
PSSMmaker.pssm(proseq,outdir)


"""
this project is from an online course available on udemy.com
called "Complete-Python3 Bootcamp"
Milestone Project 2 - Blackjack Game¶
In this milestone project you will be creating a Complete BlackJack Card Game in Python.

Here are the requirements:

You need to create a simple text-based BlackJack game
The game needs to have one player versus an automated dealer.
The player can stand or hit.
The player must be able to pick their betting amount.
You need to keep track of the player's total money.
You need to alert the player of wins, losses, or busts, etc...
#############################################################################################
all code is by George A. Merrill except where otherwise noted
version 001 text based version
version 002 start of OpenGl version
version 003 start of GUI
"""
#################################################################################################
#

from Canvas import Canvas
from PIL import Image
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import math
import time
from random import shuffle

suits = ['Spades', 'Hearts', 'Clubs', 'Diamonds']
ranks = {'Two': 2, 'Three': 3, 'Four': 4, 'Five': 5, 'Six': 6, 'Seven': 7, 'Eight': 8,
         'Nine': 9, 'Ten': 10, 'Jack': 10, 'Queen': 10, 'King':10, 'Ace': 11}
chips_player = 200
chips_dealer = 1000000
cvs = Canvas(1885, 1025, 'BlackJack')
game_mode = 'start'
current_bet = 0

class Card:
    def __init__(self, suit, rank):
        self.suit = suit
        self.rank = rank

    def __str__(self):
        return f'{self.rank} of {self.suit}'

class Deck:
    def __init__(self):
        self.cards = []
        for s in suits:
            for r in ranks:
                self.cards.append(Card(s,r))

    def shuffle(self):
        shuffle(self.cards)

def find_total(hand):
    total = 0
    aces = 0
    for r in hand:
        if r.rank == 'Ace':
            aces += 1
        total += ranks[r.rank]
    while total > 21 and aces >= 1:
        total -= 10
        aces -= 1

    return total

def place_bet():
    global current_bet
    global game_mode
    if game_mode == 'bet':
        glColor3f(0, 0, 0)
        glRasterPos2d(650, 500)
        for c in 'Minium bet is 20 chips':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
        glRecti(600, 350, 900, 450)
        glRasterPos2d(650, 475)
        for c in 'Place a bet below':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
        glRecti(600, 350, 900, 450)
        glColor3f(0.0, .6, 0.)
        glRasterPos2d(700, 400)
        for c in 'Your Current bet:':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
        glColor3f(0.9, 0.6, 0.0)
        glRasterPos2d(800, 375)
        for c in f'{current_bet}':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
    pass

def my_mouse(button, state, x, y):
    global game_mode
    if game_mode == 'start':
        game_mode = 'bet'

def my_display():
    global cvs
    cvs.set_window(0, 1885, 0, 1025)
    cvs.set_viewport(0, 1885, 0, 1025)
    if game_mode == 'start':
        cvs.set_bc(0, 0, 0)
        cvs.clear_screen()
        glColor3f(1, 1, 1)
        cvs.thick(8)
        glEnable(GL_TEXTURE_2D)
        texture = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, texture)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
        image = Image.open("res\Splash.png")
        flipped_image = image.transpose(Image.FLIP_TOP_BOTTOM)
        img_data = flipped_image.convert("RGBA").tobytes()
        # .crop((0, 0, 256, 244))
        # img_data = raw_data
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 500, 300, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_data)
        glBindTexture(GL_TEXTURE_2D, texture)
        glBegin(GL_POLYGON)
        glTexCoord2f(0, 0)
        glVertex2i(425, 200)
        glTexCoord2f(0, 1)
        glVertex2i(425, 800)
        glTexCoord2f(1, 1)
        glVertex2i(1425, 800)
        glTexCoord2f(1, 0)
        glVertex2i(1425, 200)
        glEnd()
        glDisable(GL_TEXTURE_2D)
        glColor3f(0.6, 0.0, 0.9)
        glRasterPos2d(825, 100)
        for c in 'Click Anywhere to Start':
                glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
    elif game_mode == 'bet':
        cvs.set_bc(0, 1, 0)
        cvs.clear_screen()
        glColor3f(0, 0, 0)
        glRecti(75,600, 350, 900)
        my_deck = Deck()
        my_deck.shuffle()
        hand_player = []
        hand_dealer = []
        global chips_player
        global chips_dealer
        glColor3f(0.6, 0.0, 0.9)
        glRasterPos2d(100, 850)
        for c in 'Dealers Chips:':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
        glColor3f(0.0, .6, 0.)
        glRasterPos2d(100, 750)
        for c in 'Your Chips:':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
        glColor3f(0.9, 0.6, 0.0)
        glRasterPos2d(100, 825)
        for c in f'{chips_dealer}':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
        glColor3f(0.9, 0.6, 0.0)
        glRasterPos2d(100, 725)
        for c in f'{chips_player}':
            glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, ord(c))
        # place bet
        place_bet()

    else:
        pass
    glFlush()
    cvs.swap()

# register the callback functions
glutDisplayFunc(my_display)
glutMouseFunc(my_mouse)
glutMainLoop()


#!/usr/bin/env python3
from minimodem import *
import select
import time
import subprocess
import sys
import configparser

from packet import *
from tunInterface import *

def dispatchIncomingPacketsToTun(minimodem, tun):
    packet = minimodem.getIncomingPackets()
    if packet[0] > 0:
        print('Sending to TUN : '+str(packet[1]))
        tun.writePacket(packet[1][3])

if __name__ == '__main__':
    config = configparser.ConfigParser()
    config.read(sys.argv[1])

    pkt = Packetizer(callsign=config['radio']['callsign'])

    mm = minimodem_wrapper(speed = 1200, interface=config['radio']['audiodevice'])
    tun = tunInterface(config = config['tun'])

    while(True):
        readers, writers, _ = select.select([mm.rxHandler.stdout, mm.rxHandler.stderr, mm.txHandler.stderr, tun.tun], [], [], 5)
        if mm.rxHandler.stderr in readers:
            mm.processRxPacketTags()

        if mm.txHandler.stderr in readers:
            mm.processEndOfTransmission()

        if mm.rxHandler.stdout in readers:
            mm.processRxPacketData()

        if tun.tun in readers:
            data = tun.getPacket()
            packet = pkt.createPacket(data)
            print ("Sending to Radio: "+str(packet))
            mm.send(packet)

        # Send data on timeout
        mm.processTxPackets()

        # Dispatch incoming packets to tun
        dispatchIncomingPacketsToTun(mm, tun)


import sys
from sakura.common.streams import on_end_of_redirection

CURRENT_PDB_SESSION = None

def hook_pdb():
    import pdb
    class HookedPdb(pdb.Pdb):
        def set_trace(self, frame):
            global CURRENT_PDB_SESSION
            if CURRENT_PDB_SESSION is not None and CURRENT_PDB_SESSION is not self:
                print('-- pdb.set_trace() FAILED. --')
                print('Another developer is currently running a pdb session on the same sakura daemon.')
                print('Cannot run another concurrent session, sorry. Please retry later.')
                print('Continuing execution.')
                print('-----------------------------')
                return None
            CURRENT_PDB_SESSION = self
            on_end_of_redirection(self.end_of_redirection)
            super().set_trace(frame)
        def end_of_redirection(self):
            sys.__stdout__.write("pdb: end of stdin/out/err redirection -- forcing 'continue' command!\n")
            self.cmdqueue.append('cont')
            on_end_of_redirection(None)
        def _release_session(self):
            global CURRENT_PDB_SESSION
            if CURRENT_PDB_SESSION is self:
                CURRENT_PDB_SESSION = None
                on_end_of_redirection(None)
        def set_continue(self):
            self._release_session()
            return super().set_continue()
        def __del__(self):
            self._release_session()
            if hasattr(super(), '__del__'):
                super().__del__()

    pdb.Pdb = HookedPdb