from dataclasses import dataclass, field
from typing import Optional
from ojp.parameterised_action_structure import ParameterisedActionStructure

__NAMESPACE__ = "http://www.siri.org.uk/siri"

@dataclass
class PublishToTvActionStructure(ParameterisedActionStructure):
    """
    Type for Notify SITUATION to Tv.

    :ivar ceefax: Publish to Teltext. Default is 'true'.
    :ivar teletext: Publish to Ceefax. Default is 'true'.
    """
    ceefax: Optional[bool] = field(
        default=None,
        metadata={
            "name": "Ceefax",
            "type": "Element",
            "namespace": "http://www.siri.org.uk/siri",
        }
    )
    teletext: Optional[bool] = field(
        default=None,
        metadata={
            "name": "Teletext",
            "type": "Element",
            "namespace": "http://www.siri.org.uk/siri",
        }
    )


import re

sentences = []

with open('input.txt', encoding='utf-8') as file:
	for line in file:
		line_sentences = re.split('[.?!]', line)
		for sentence in line_sentences:
			sentence = sentence.strip()
			if len(sentence) > 0:
				sentence = re.sub('[,:;-]', '', sentence)
				sentences.append(sentence)

def change_word(word):
	new_word = [letter * word.count(letter) for letter in word]
	return ''.join(new_word)

def change_sentence(sentence):
	words = [change_word(word) for word in sentence.split(' ')]
	return ' '.join(words)

sentences = [change_sentence(s) for s in sentences]

with open('output.txt', 'w', encoding='utf-8') as file:
	for sentence in sentences:
		file.write(sentence + '\n')


import genai_core.clients

from langchain.llms import Bedrock
from langchain.prompts.prompt import PromptTemplate

from ..base import ModelAdapter
from ..registry import registry

class BedrockClaudeAdapter(ModelAdapter):
    def __init__(self, model_id, *args, **kwargs):
        self.model_id = model_id

        super().__init__(*args, **kwargs)

    def get_llm(self, model_kwargs={}):
        bedrock = genai_core.clients.get_bedrock_client()

        params = {}
        if "temperature" in model_kwargs:
            params["temperature"] = model_kwargs["temperature"]
        if "topP" in model_kwargs:
            params["top_p"] = model_kwargs["topP"]
        if "maxTokens" in model_kwargs:
            params["max_tokens_to_sample"] = model_kwargs["maxTokens"]

        return Bedrock(
            client=bedrock,
            model_id=self.model_id,
            model_kwargs=params,
            streaming=model_kwargs.get("streaming", False),
            callbacks=[self.callback_handler],
        )

    def get_qa_prompt(self):
        template = """

Human: Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.

{context}

Question: {question}

Assistant:"""

        return PromptTemplate(
            template=template, input_variables=["context", "question"]
        )

    def get_prompt(self):
        template = """

Human: The following is a friendly conversation between a human and an AI. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
{chat_history}

Question: {input}

Assistant:"""

        input_variables = ["input", "chat_history"]
        prompt_template_args = {
            "chat_history": "{chat_history}",
            "input_variables": input_variables,
            "template": template,
        }
        prompt_template = PromptTemplate(**prompt_template_args)

        return prompt_template

    def get_condense_question_prompt(self):
        template = """
{chat_history}

Human: Given the above conversation and a follow up input, rephrase the follow up input to be a standalone question, in the same language as the follow up input.
Follow Up Input: {question}

Assistant:"""

        return PromptTemplate(
            input_variables=["chat_history", "question"],
            chat_history="{chat_history}",
            template=template,
        )

# Register the adapter
registry.register(r"^bedrock.anthropic.claude*", BedrockClaudeAdapter)


#! /usr/bin/env python
# coding=utf-8
"""
Author: Deean
Date: 2022-02-17 00:08:36
LastEditTime: 2022-02-17 00:10:17
Description:
FilePath: 145.二叉树的后序遍历.py
"""
#
# @lc app=leetcode.cn id=145 lang=python3
#
# [145] 二叉树的后序遍历
#

# @lc code=start
from typing import List
from typing import Optional

# Definition for a binary tree node.
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        else:
            return (
                    self.postorderTraversal(root.left)
                    + self.postorderTraversal(root.right)
                    + [root.val]
            )

# @lc code=end


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


# cook your dish here
def multiple(number):
    l = [0,5]
    for i in number:
        if i in l:
            return 'YES'
    return 'NO'

for i in range(int(input())):
    k = int(input())
    number = [int(i) for i in input()]
    print(multiple(number))


from .core.camtek import CamtekInfo
import re
import configparser
import os.path as osp
from pathlib import Path

class SananAoiInfo(CamtekInfo):
    @property
    def product_name(self) -> str:
        """ Return the product name (eg: '8-X68E42B-FI23') """
        assert 'image_path' in self.aoi_info
        product_name = self.get_product_name_from_image_path(self.aoi_info['image_path'])
        return product_name

    @staticmethod
    def get_product_name_from_image_path(image_path: str):
        PRODUCT_NAME_PATTERN = r'[A-Z]{1}\w+\d+'
        product_names = re.findall(PRODUCT_NAME_PATTERN, image_path)
        product_names = set(x for x in product_names)
        if len(product_names) == 1:
            return product_names.pop()
        else:
            raise RuntimeError("Can not find a product name for path: {}".format(image_path))

    @property
    def magnification(self) -> float:
        """
        Lens magnification ratio (eg: 5x, 10x), which is negtively relative to the pixel size.
        Relations of the lens mag and the pixel size differ among different AOI device manufacturers.
        """
        return self.aoi_info['magnification']

    @classmethod
    def from_image_path(cls, image_path):
        assert osp.exists(image_path), 'image_path {} not found'.format(image_path)

        # get ProductInfo
        image_path_instance = Path(image_path)
        file_name = image_path_instance.name
        product_info = str(image_path_instance.with_name('ProductInfo.ini'))
        colorImageGrabingInfo = str(image_path_instance.with_name('ColorImageGrabingInfo.ini'))
        assert osp.exists(product_info), "product_info file {} not found.".format(product_info)
        assert osp.exists(colorImageGrabingInfo), "colorImageGrabingInfo file {} not found.".format(colorImageGrabingInfo)
        ini_parser = configparser.ConfigParser(strict=False)
        ini_parser.read(product_info)

        die_size_col = float(ini_parser.get('Geometric', 'XDieIndex'))
        die_size_row = float(ini_parser.get('Geometric', 'YDieIndex'))

        ## suggested by du yanwei
        # die_size_col = float(ini_parser.get('Geometric', 'CustomerDiePitch_X'))
        # die_size_row = float(ini_parser.get('Geometric', 'CustomerDiePitch_Y'))

        # get colorImageGrabingInfo
        ini_parser.read(colorImageGrabingInfo)
        image_pix_size_col = None
        image_pix_size_row = None
        if ini_parser.has_option(file_name, 'PixelSizeX'):
            image_pix_size_col = float(ini_parser.get(file_name, 'PixelSizeX'))
            image_pix_size_row = float(ini_parser.get(file_name, 'PixelSizeY'))
            pix_col = int(ini_parser.get(file_name, 'Col'))
            pix_row = int(ini_parser.get(file_name, 'Row'))
            image_size_col = int(ini_parser.get(file_name, 'ImageSizeX'))
            image_size_row = int(ini_parser.get(file_name, 'ImageSizeY'))
            col = float(ini_parser.get(file_name, 'X'))
            row = float(ini_parser.get(file_name, 'Y'))
            magnification = float(ini_parser.get(file_name, 'Mag'))
        else:
            for sess in ini_parser.sections():
                if sess.endswith(Path(image_path).suffix) and ini_parser.has_option(sess, 'PixelSizeX'):
                    image_pix_size_col = float(ini_parser.get(sess, 'PixelSizeX'))
                    image_pix_size_row = float(ini_parser.get(sess, 'PixelSizeY'))
                    pix_col = int(ini_parser.get(sess, 'Col'))
                    pix_row = int(ini_parser.get(sess, 'Row'))
                    image_size_col = int(ini_parser.get(sess, 'ImageSizeX'))
                    image_size_row = int(ini_parser.get(sess, 'ImageSizeY'))
                    magnification = float(ini_parser.get(file_name, 'Mag'))
                    break
            # get image offset
            col = float(file_name.split('.')[0])
            row = float(file_name.split('.')[1])

        if image_pix_size_col is None or image_pix_size_row is None:
            raise Exception("PixelSizeX({}) and PixelSizeY({}) must be not None.".format(image_pix_size_col, image_pix_size_row))

        die_col = round((col - 0.5 * die_size_col) / die_size_col)
        die_row = round((row - 0.5 * die_size_row) / die_size_row)

        aoi_info = dict(
            image_path=image_path,
            file_name=file_name,
            die_size_col=die_size_col,
            die_size_row=die_size_row,
            image_pix_size_col=image_pix_size_col,
            image_pix_size_row=image_pix_size_row,
            pix_col=pix_col,
            pix_row=pix_row,
            die_col=die_col,
            die_row=die_row,
            image_size_col=image_size_col,
            image_size_row=image_size_row,
            col = col,
            row = row,
            magnification = magnification
        )

        return cls(aoi_info)


#!/usr/bin/python
"""Progress Bar 3

Frameless and no title bar.
Centred in the screen.

Bigger font size.
Bigger width progress bar.
"""

__version__ = "1.0"
__author__ = "simon.peverett@gmail.com"
__all__ = ['__version__', '__author__']

import sys
if (sys.version_info > (3, 0)):
    from tkinter import *
    from tkinter.font import Font
    from tkinter.ttk import Progressbar
else:
    from Tkinter import *
    from tkFont import Font
    from ttk import Progressbar

class FancyProgressBar(object):
    def __init__(self, parent):
        """A fancy progress bar"""
        self.parent = parent

        self.font = Font(family="Consolas", size=36)
        self.value = StringVar()

        self.frm = Frame( parent, bd=5, height=300, width=200, bg="black" )
        self.frm.pack(side=TOP, fill=X, expand=NO)

        self.legend = Label(
                self.frm, text="Downloading virus...", width=30, justify=CENTER,
                padx=5, pady=5, bg="black", fg="green2", font=self.font
                )
        self.legend.pack(side=TOP, fill=X, expand=YES)

        self.pf = Frame( self.frm, bd=20, bg="black")
        self.pf.pack(side=TOP, fill=X, expand=YES)
        self.progress = Progressbar(
                self.pf, orient="horizontal", mode="determinate",
                length=200
                )
        self.progress.pack(side=TOP, fill=X, ipady=25)
        self.progress["maximum"] = 100
        self.value.set( "{} %".format(self.progress["value"]) )

        self.pg_text = Label(
                self.frm, textvariable=self.value, width=30, justify=CENTER,
                padx=5, pady=6, bg="black", fg="green2", font=self.font
                )
        self.pg_text.pack(side=TOP, fill=X, expand=NO)

        self.frm.after(100, self.auto_inc)

    def auto_inc(self):
        self.progress.step()
        self.value.set( "{} %".format( int(self.progress["value"])) )
        if self.progress["value"] < 99:
            self.frm.after(100, self.auto_inc)
        else:
            self.progress["value"] = 100
            self.value.set( "Complete." )
            self.frm.after(2000, self.destroy)

    def destroy(self):
        self.parent.destroy()

def centre(toplevel):
    toplevel.update_idletasks()

    # Tkinter way to find the screen resolution
    screen_width = toplevel.winfo_screenwidth()
    screen_height = toplevel.winfo_screenheight()

    # PyQt way to find the screen resolution
    # app = QtGui.QApplication([])
    # screen_width = app.desktop().screenGeometry().width()
    # screen_height = app.desktop().screenGeometry().height()

    size = tuple(int(_) for _ in toplevel.geometry().split('+')[0].split('x'))
    x = screen_width/2 - size[0]/2
    y = screen_height/2 - size[1]/2

    toplevel.geometry("+%d+%d" % (x, y))

def main():
    """Main function"""
    root = Tk()

    # Tell the window manager this is a splash window, so no border and no
    # title bar.
    root.wm_overrideredirect(True)
    control = FancyProgressBar(root)
    centre(root)
    root.mainloop()

if __name__ == "__main__":
    main()


import pace.util
from ._wrapper import (
    _get_diagnostic_data,
    _get_diagnostic_info,
    initialize,
    step,
    step_dynamics,
    step_radiation,
    step_pre_radiation,
    step_post_radiation_physics,
    step_physics,
    save_intermediate_restart_if_enabled,
    save_fortran_restart,
    cleanup,
    get_state,
    set_state,
    get_n_ghost_cells,
    get_step_count,
    get_tracer_metadata,
    compute_physics,
    apply_physics,
    flags,
    DiagnosticInfo,
    transform_agrid_winds_to_dgrid_winds,
    transform_dgrid_winds_to_agrid_winds
)
from ._restart import get_restart_names, open_restart
from . import examples

from .thermodynamics import set_state_mass_conserving

def get_diagnostic_by_name(
    name: str, module_name: str = "gfs_phys"
) -> pace.util.Quantity:
    """Get a diagnostic field as a Quantity

    Currently, only supports diagnostics defined in the FV3GFS_io.F90
    """
    metadata = get_diagnostic_metadata_by_name(name, module_name)
    return _get_diagnostic_data(metadata.is_diag_manager_controlled, metadata.index)

def get_diagnostic_metadata_by_name(
    name: str, module_name: str = "gfs_phys"
) -> DiagnosticInfo:
    """Get diagnostic metadata by name

    Currently, only supports diagnostics defined in the FV3GFS_io.F90
    """
    info = _get_diagnostic_info()

    key = module_name, name
    if key in info:
        return info[key]
    else:
        raise ValueError(f"There is no diagnostic {name} in module {module_name}.")

__version__ = "0.1.0"

__all__ = list(key for key in locals().keys() if not key.startswith("_"))


"""
Tests reading from database.
"""
import datetime
from pytest import fixture
from dbfread import DBF

@fixture
def table():
    return DBF('tests/cases/memotest.dbf')

@fixture
def loaded_table():
    return DBF('tests/cases/memotest.dbf', load=True)

# This relies on people.dbf having this exact content.
records = [{u'NAME': u'Alice',
            u'BIRTHDATE': datetime.date(1987, 3, 1),
            u'MEMO': u'Alice memo'},
           {u'NAME': u'Bob',
            u'BIRTHDATE': datetime.date(1980, 11, 12),
            u'MEMO': u'Bob memo'}]
deleted_records = [{u'NAME': u'Deleted Guy',
                    u'BIRTHDATE': datetime.date(1979, 12, 22),
                    u'MEMO': u'Deleted Guy memo'}]

def test_len(table, loaded_table):
    assert len(table) == 2
    assert len(table.deleted) == 1

    assert len(loaded_table) == 2
    assert len(loaded_table.deleted) == 1

def test_list(table, loaded_table):
    assert list(table) == records
    assert list(table.deleted) == deleted_records

    assert list(loaded_table) == records
    assert list(loaded_table.deleted) == deleted_records

    # This should not return old style table which was a subclass of list.
    assert not isinstance(table, list)


"""
В массиве случайных целых чисел поменять местами минимальный и максимальный элементы.
"""

import random

SIZE = 10
MIN_ITEM = 0
MAX_ITEM = 100
array = [random.randint(MIN_ITEM, MAX_ITEM) for _ in range(SIZE)]
print(array)

max_el_inx = 0
max_el = array[0]
min_el_inx = 0
min_el = array[0]
for i, item in enumerate(array):
    if item > max_el:
        max_el_inx = i
        max_el = item
    elif item < min_el:
        min_el_inx = i
        min_el = item
array[min_el_inx], array[max_el_inx] = array[max_el_inx], array[min_el_inx]
print(array)


from functools import wraps
import hashlib
import logging
from operator import itemgetter
import os
import re
from unicodedata import normalize as unicodedata_normalize
import unicodedata

from crispy_forms.layout import HTML, Button
from django import forms
from django.apps import apps
from django.conf import settings
from django.contrib import admin
from django.contrib.contenttypes.fields import (GenericForeignKey, GenericRel,
                                                GenericRelation)
from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.core.files.uploadedfile import UploadedFile
from django.core.mail import get_connection
from django.db import models
from django.db.models import Q
from django.db.models.fields.files import FieldFile
from django.forms import BaseForm
from django.forms.widgets import SplitDateTimeWidget
from django.http.response import HttpResponse
from django.template import loader
from django.urls.base import reverse
from django.utils import six, timezone
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
import django_filters
from easy_thumbnails import source_generators
from floppyforms.widgets import ClearableFileInput
from image_cropping.widgets import ImageCropWidget
import magic
from unipath.path import Path
import weasyprint

from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row

# (26/10/2018): O separador foi mudador de '/' para 'K'
# por conta dos leitores de códigos de barra, que trocavam
# a '/' por '&' ou ';'
SEPARADOR_HASH_PROPOSICAO = 'K'

def dont_break_out(value, max_part=50):
    _safe = value.split()

    def chunkstring(string):
        return re.findall('.{%d}' % max_part, string)

    def __map(a):
        if len(a) <= max_part:
            return a
        return '
' + '
'.join(chunkstring(a))

    _safe = map(__map, _safe)
    _safe = ' '.join(_safe)

    _safe = mark_safe(_safe)
    return value

def clear_thumbnails_cache(queryset, field):

    for r in queryset:
        assert hasattr(r, field), _(
            'Objeto da listagem não possui o campo informado')

        if not getattr(r, field):
            continue

        path = Path(getattr(r, field).path)

        if not path.exists():
            continue

        cache_files = path.parent.walk()

        for cf in cache_files:
            if cf != path:
                cf.remove()

def normalize(txt):
    return unicodedata_normalize(
        'NFKD', txt).encode('ASCII', 'ignore').decode('ASCII')

def get_settings_auth_user_model():
    return getattr(settings, 'AUTH_USER_MODEL', 'auth.User')

autor_label = '''

       Autor:
                {% if form.autor.value %}
                    {{form.autor.value}}
                {% endif %}

'''

autor_modal = '''

       <form>
           <input id="q" type="text" />
           <input id="pesquisar" type="submit" value="Pesquisar"
               class="btn btn-primary btn-sm"/>
       </form>

       <input type="submit" id="selecionar" value="Selecionar"
              hidden="true" />

'''

def montar_row_autor(name):
    autor_row = to_row(
        [(name, 0),
         (Button('pesquisar',
                 'Pesquisar Autor',
                 css_class='btn btn-secondary btn-sm'), 2),
         (Button('limpar',
                 'Limpar Autor',
                 css_class='btn btn-secondary btn-sm'), 10)])

    return autor_row

# TODO: Esta função é utilizada?

def montar_helper_autor(self):
    autor_row = montar_row_autor('nome')
    self.helper = SaplFormHelper()
    self.helper.layout = SaplFormLayout(*self.get_layout())

    # Adiciona o novo campo 'autor' e mecanismo de busca
    self.helper.layout[0][0].append(HTML(autor_label))
    self.helper.layout[0][0].append(HTML(autor_modal))
    self.helper.layout[0][1] = autor_row

    # Adiciona espaço entre o novo campo e os botões
    # self.helper.layout[0][4][1].append(HTML('

'))

    # Remove botões que estão fora do form
    self.helper.layout[1].pop()

    # Adiciona novos botões dentro do form
    self.helper.layout[0][4][0].insert(2, form_actions(more=[
        HTML('Cancelar')]))

class SaplGenericForeignKey(GenericForeignKey):

    def __init__(
        self,
            ct_field='content_type',
            fk_field='object_id',
            for_concrete_model=True,
            verbose_name=''):
        super().__init__(ct_field, fk_field, for_concrete_model)
        self.verbose_name = verbose_name

class SaplGenericRelation(GenericRelation):
    """
    Extenção da class GenericRelation para implmentar o atributo fields_search

    fields_search é uma tupla de tuplas de dois strings no padrão de construção
        de campos porém com [Field Lookups][ref_1]

        exemplo:
            [No Model Parlamentar em][ref_2] existe a implementação dessa
            classe no atributo autor. Parlamentar possui três informações
            relevantes para buscas realacionadas a Autor:

                - nome_completo;
                - nome_parlamentar; e
                - filiacao__partido__sigla

            que devem ser pesquisados, coincidentemente
            pelo FieldLookup __icontains

            portanto a estrutura de fields_search seria:
                fields_search=(
                    ('nome_completo', '__icontains'),
                    ('nome_parlamentar', '__icontains'),
                    ('filiacao__partido__sigla', '__icontains'),
                )

    [ref_1]: https://docs.djangoproject.com/el/1.10/topics/db/queries/
             #field-lookups
    [ref_2]: https://github.com/interlegis/sapl/blob/master/sapl/
             parlamentares/models.py
    """

    def __init__(self, to, fields_search=(), **kwargs):

        assert 'related_query_name' in kwargs, _(
            'SaplGenericRelation não pode ser instanciada sem '
            'related_query_name.')

        assert fields_search, _(
            'SaplGenericRelation não pode ser instanciada sem fields_search.')

        for field in fields_search:
            # descomente para ver todas os campos que são elementos de busca
            # print(kwargs['related_query_name'], field)

            assert isinstance(field, (tuple, list)), _(
                'fields_search deve ser um array de tuplas ou listas.')

            assert len(field) <= 3, _(
                'cada tupla de fields_search deve possuir até 3 strings')

            # TODO implementar assert para validar campos do Model e lookups

        self.fields_search = fields_search
        super().__init__(to, **kwargs)

class OverwriteStorage(FileSystemStorage):

    def save(self, name, content, max_length=None):

        prod_name = FileSystemStorage.save(
            self, name, content, max_length=max_length)

        FileSystemStorage.save(
            self, 'original__%s' % prod_name, content, max_length=max_length)

        return prod_name

class PortalFieldFile(FieldFile):

    @property
    def original_path(self):
        self._require_file()
        return self.storage.path('original__{}'.format(self.name))

    @property
    def original_name(self):
        self._require_file()
        return 'original__{}'.format(self.name)

    @property
    def url(self):
        try:
            self._require_file()
            # if settings.DEBUG:
            #    return self.storage.url(self.name)

            field_name_action = self.field.name.replace('_', '-')
            return '%s' % reverse(
                'sapl.api:%s-%s' % (self.instance._meta.model_name,
                                    field_name_action),
                kwargs={'pk': self.instance.pk})
        except:
            return ''

    def shorten_file_name(self):
        name = self.name

        if not name:
            return

        nl = name.split('/')

        if len(nl[-1]) < 128:
            return

        name = nl[-1][-20:]

        name = f'f_{name}'

        nl[-1] = name
        new_path = f"{settings.MEDIA_ROOT}/{'/'.join(nl)}"
        new_original_path = new_path.replace('sapl/', 'original__sapl/')
        new_original_path = new_original_path.replace('cmj/', 'original__cmj/')

        os.rename(self.path, new_path)
        os.rename(self.original_path, new_original_path)

        self.name = '/'.join(nl)
        self.instance.save()

    def delete(self, save=True):
        if not self:
            return
        # Only close the file if it's already open, which we know by the
        # presence of self._file
        if hasattr(self, '_file'):
            self.close()
            del self.file

        original_name = self.name.replace('sapl/', 'original__sapl/')
        original_name = original_name.replace('cmj/', 'original__cmj/')
        self.storage.delete(self.name)
        self.storage.delete(original_name)

        self.name = None
        setattr(self.instance, self.field.name, self.name)
        self._committed = False

        if save:
            self.instance.save()

class PortalFileField(models.FileField):
    attr_class = PortalFieldFile

class ImageThumbnailFileInput(ClearableFileInput):
    template_name = 'floppyforms/image_thumbnail.html'

class CustomImageCropWidget(ImageCropWidget):
    """
    Custom ImageCropWidget that doesn't show the initial value of the field.
    We use this trick, and place it right under the CropWidget so that
    it looks like the user is seeing the image and clearing the image.
    """
    template_with_initial = (
        # '%(initial_text)s: %(initial)s '
        '%(clear_template)s
%(input_text)s: %(input)s'
    )

class RangeWidgetOverride(forms.MultiWidget):

    def __init__(self, attrs=None):
        widgets = (forms.DateInput(format='%d/%m/%Y',
                                   attrs={'class': 'dateinput form-control',
                                          'placeholder': 'Inicial'}),
                   forms.DateInput(format='%d/%m/%Y',
                                   attrs={'class': 'dateinput form-control',
                                          'placeholder': 'Final'}))
        super(RangeWidgetOverride, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.start, value.stop]
        return []

    def render(self, name, value, attrs=None, renderer=None):
        rendered_widgets = []
        for i, x in enumerate(self.widgets):
            rendered_widgets.append(
                x.render(
                    '%s_%d' % (name, i), value[i] if value else ''
                )
            )

        html = '
%s

%s
'\
            % tuple(rendered_widgets)
        return '
%s
' % html

class CustomSplitDateTimeWidget(SplitDateTimeWidget):

    def render(self, name, value, attrs=None, renderer=None):
        rendered_widgets = []
        for i, x in enumerate(self.widgets):
            x.attrs['class'] += ' form-control'
            rendered_widgets.append(
                x.render(
                    '%s_%d' % (name, i), self.decompress(
                        value)[i] if value else ''
                )
            )

        html = '
%s

%s
'\
            % tuple(rendered_widgets)
        return '
%s
' % html

def register_all_models_in_admin(module_name, exclude_list=[]):
    appname = module_name.split('.')
    appname = appname[1] if appname[0] == 'sapl' else appname[0]
    app = apps.get_app_config(appname)
    for model in app.get_models():
        if not admin.site.is_registered(model):
            admin.site.register(model)

def xstr(s):
    return '' if s is None else str(s)

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

def get_base_url(request):
    # TODO substituir por Site.objects.get_current().domain
    # from django.contrib.sites.models import Site

    current_domain = request.get_host()
    protocol = 'https' if request.is_secure() else 'http'
    return "{0}://{1}".format(protocol, current_domain)

def create_barcode(value, width=170, height=50):
    '''
        creates a base64 encoded barcode PNG image
    '''
    from base64 import b64encode
    from reportlab.graphics.barcode import createBarcodeDrawing
    value_bytes = bytes(value, "ascii")
    barcode = createBarcodeDrawing('Code128',
                                   value=value_bytes,
                                   barWidth=width,
                                   height=height,
                                   fontSize=2,
                                   humanReadable=True)
    data = b64encode(barcode.asString('png'))
    return data.decode('utf-8')

YES_NO_CHOICES = [(True, _('Sim')), (False, _('Não'))]

def listify(function):

    @wraps(function)
    def f(*args, **kwargs):
        return list(function(*args, **kwargs))

    return f

LISTA_DE_UFS = [
    ('AC', 'Acre'),
    ('AL', 'Alagoas'),
    ('AP', 'Amapá'),
    ('AM', 'Amazonas'),
    ('BA', 'Bahia'),
    ('CE', 'Ceará'),
    ('DF', 'Distrito Federal'),
    ('ES', 'Espírito Santo'),
    ('GO', 'Goiás'),
    ('MA', 'Maranhão'),
    ('MT', 'Mato Grosso'),
    ('MS', 'Mato Grosso do Sul'),
    ('MG', 'Minas Gerais'),
    ('PR', 'Paraná'),
    ('PB', 'Paraíba'),
    ('PA', 'Pará'),
    ('PE', 'Pernambuco'),
    ('PI', 'Piauí'),
    ('RJ', 'Rio de Janeiro'),
    ('RN', 'Rio Grande do Norte'),
    ('RS', 'Rio Grande do Sul'),
    ('RO', 'Rondônia'),
    ('RR', 'Roraima'),
    ('SC', 'Santa Catarina'),
    ('SE', 'Sergipe'),
    ('SP', 'São Paulo'),
    ('TO', 'Tocantins'),
    ('EX', 'Exterior'),
]

RANGE_ANOS = [(year, year) for year in range(timezone.now().year + 1,
                                             1889, -1)]

RANGE_MESES = [
    (1, 'Janeiro'),
    (2, 'Fevereiro'),
    (3, 'Março'),
    (4, 'Abril'),
    (5, 'Maio'),
    (6, 'Junho'),
    (7, 'Julho'),
    (8, 'Agosto'),
    (9, 'Setembro'),
    (10, 'Outubro'),
    (11, 'Novembro'),
    (12, 'Dezembro'),
]

RANGE_DIAS_MES = [(n, n) for n in range(1, 32)]

def ANO_CHOICES():
    return [('', '---------')] + RANGE_ANOS

def choice_anos(model):
    try:
        anos_list = model.objects.all().distinct(
            'ano').order_by('-ano').values_list('ano', 'ano')
        return list(anos_list)
    except Exception:
        return []

def choice_anos_com_materias():
    from sapl.materia.models import MateriaLegislativa
    return choice_anos(MateriaLegislativa)

def choice_anos_com_normas():
    from sapl.norma.models import NormaJuridica
    return choice_anos(NormaJuridica)

def choice_anos_com_protocolo():
    from sapl.protocoloadm.models import Protocolo
    return choice_anos(Protocolo)

def choice_anos_com_documentoadministrativo():
    from sapl.protocoloadm.models import DocumentoAdministrativo
    return choice_anos(DocumentoAdministrativo)

def choice_anos_com_sessaoplenaria():
    try:
        from sapl.sessao.models import SessaoPlenaria
        y = SessaoPlenaria.objects.all().dates('data_inicio', 'year')

        y = list(map(lambda x: x.year, y))

        now = timezone.now()

        if now.year not in y:
            y.append(now.year)

        y.sort(reverse=True)

        y = map(lambda x: (x, x), y)

        return y
    except Exception:
        return []

def choice_force_optional(callable):
    """ Django-filter faz algo que tenha o mesmo sentido em ChoiceFilter,
        no entanto, as funções choice_anos_... podem ser usadas em formulários
        comuns de adição e/ou edição, com a particularidade de terem
        required=False.
        Neste caso para ser possível contar com a otimização de apenas mostrar anos
        que estejam na base de dados e ainda colocar o item opcional '---------',
        é necessário encapsular então, as funções choice_anos_... com a
        esta função choice_force_optional... isso ocorre e foi aplicado
        inicialmente no cadastro de documentos administrativos onde tem-se
        opcionalmente a possibilidade de colocar o ano do protocolo.
        Em ChoiceFilter choice_force_optional não deve ser usado pois duplicaria
        o item opcional '---------' já que ChoiceFilter já o adiciona, como dito
        anteriormente.
    """

    def _func():
        return [('', '---------')] + callable()

    return _func

FILTER_OVERRIDES_DATEFIELD = {
    'filter_class': django_filters.DateFromToRangeFilter,
    'extra': lambda f: {
        'label': '%s (%s)' % (f.verbose_name, _('Inicial - Final')),
        'widget': RangeWidgetOverride
    }
}

class FilterOverridesMetaMixin:
    filter_overrides = {
        models.DateField: FILTER_OVERRIDES_DATEFIELD
    }

TIPOS_TEXTO_PERMITIDOS = (
    'application/vnd.oasis.opendocument.text',
    'application/x-vnd.oasis.opendocument.text',
    'application/pdf',
    'application/x-pdf',
    'application/zip',
    'application/acrobat',
    'applications/vnd.pdf',
    'text/pdf',
    'text/x-pdf',
    'text/plain',
    'application/txt',
    'browser/internal',
    'text/anytext',
    'widetext/plain',
    'widetext/paragraph',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/xml',
    'application/octet-stream',
    'text/xml',
    'text/html',
    'video/mp4'
)

TIPOS_IMG_PERMITIDOS = (
    'image/jpeg',
    'image/jpg',
    'image/jpe_',
    'image/pjpeg',
    'image/vnd.swiftview-jpeg',
    'application/jpg',
    'application/x-jpg',
    'image/pjpeg',
    'image/pipeg',
    'image/vnd.swiftview-jpeg',
    'image/x-xbitmap',
    'image/bmp',
    'image/x-bmp',
    'image/x-bitmap',
    'image/png',
    'application/png',
    'application/x-png',
)

def fabrica_validador_de_tipos_de_arquivo(lista, nome):

    def restringe_tipos_de_arquivo(value):
        if not os.path.splitext(value.path)[1][:1]:
            raise ValidationError(_(
                'Não é possível fazer upload de arquivos sem extensão.'))
        try:
            mime = magic.from_buffer(value.read(), mime=True)
            if mime not in lista:
                raise ValidationError(_('Tipo de arquivo não suportado'))
        except FileNotFoundError:
            raise ValidationError(_('Arquivo não encontrado'))

    # o nome é importante para as migrations
    restringe_tipos_de_arquivo.__name__ = nome
    return restringe_tipos_de_arquivo

restringe_tipos_de_arquivo_txt = fabrica_validador_de_tipos_de_arquivo(
    TIPOS_TEXTO_PERMITIDOS, 'restringe_tipos_de_arquivo_txt')
restringe_tipos_de_arquivo_img = fabrica_validador_de_tipos_de_arquivo(
    TIPOS_IMG_PERMITIDOS, 'restringe_tipos_de_arquivo_img')

def intervalos_tem_intersecao(a_inicio, a_fim, b_inicio, b_fim):
    maior_inicio = max(a_inicio, b_inicio)
    menor_fim = min(a_fim, b_fim)
    return maior_inicio <= menor_fim

class MateriaPesquisaOrderingFilter(django_filters.OrderingFilter):

    choices = (
        ('dataD', 'Ordem Decrescente - Data, Tipo, Ano, Numero '),
        ('tipoD', 'Ordem Decrescente - Tipo, Ano, Numero, Data'),
        ('dataC', 'Ordem Crescente - Data, Tipo, Ano, Numero'),
        ('tipoC', 'Ordem Crescente - Tipo, Ano, Numero, Data'),
    )
    order_by_mapping = {
        'dataD': ['-data_apresentacao', '-tipo__sigla', '-ano', '-numero'],
        'tipoD': ['-tipo__sigla', '-ano', '-numero', '-data_apresentacao'],
        'dataC': ['data_apresentacao', 'tipo__sigla', 'ano', 'numero'],
        'tipoC': ['tipo__sigla', 'ano', 'numero', 'data_apresentacao'],
    }

    def __init__(self, *args, **kwargs):
        kwargs['choices'] = self.choices
        super(MateriaPesquisaOrderingFilter, self).__init__(*args, **kwargs)

    def filter(self, qs, value):
        if value:
            _value = self.order_by_mapping[value[0]] if value else value
        else:
            _value = ['-data_apresentacao', '-id']

        return super().filter(qs, _value)

class NormaPesquisaOrderingFilter(django_filters.OrderingFilter):

    choices = (
        ('dataD', 'Ordem Decrescente - Data, Tipo, Ano, Numero '),
        ('tipoD', 'Ordem Decrescente - Tipo, Ano, Numero, Data'),
        ('dataC', 'Ordem Crescente - Data, Tipo, Ano, Numero'),
        ('tipoC', 'Ordem Crescente - Tipo, Ano, Numero, Data'),
    )
    order_by_mapping = {
        'dataD': ['-data', '-tipo', '-ano', '-numero'],
        'tipoD': ['-tipo', '-ano', '-numero', '-data'],
        'dataC': ['data', 'tipo', 'ano', 'numero'],
        'tipoC': ['tipo', 'ano', 'numero', 'data'],
    }

    def __init__(self, *args, **kwargs):
        kwargs['choices'] = self.choices
        super(NormaPesquisaOrderingFilter, self).__init__(*args, **kwargs)

    def filter(self, qs, value):
        if value:
            _value = self.order_by_mapping[value[0]] if value else value
        else:
            _value = self.order_by_mapping['dataD']
        return super().filter(qs, _value)

class FileFieldCheckMixin(BaseForm):

    def _check(self):
        cleaned_data = super(FileFieldCheckMixin, self).clean()
        errors = []
        for name, campo in self.fields.items():
            if isinstance(campo, forms.fields.FileField):
                error = self.errors.get(name)
                if error:
                    msgs = [e.replace('Certifique-se de que o arquivo',
                                      "Certifique-se de que o nome do "
                                      "arquivo no campo '{}'".format(
                                          campo.label))
                            for e in error]
                    for msg in msgs:
                        errors.append(msg)

                arquivo = self.cleaned_data.get(name)
                if arquivo and not isinstance(arquivo, UploadedFile):
                    if not os.path.exists(arquivo.path):
                        errors.append("Arquivo referenciado no campo "
                                      " '%s' inexistente! Marque a "
                                      "opção Limpar e Salve." % campo.label)
        if errors:
            raise ValidationError(errors)
        return cleaned_data

    def clean(self):
        """ Alias for _check() """
        return self._check()

class AnoNumeroOrderingFilter(django_filters.OrderingFilter):

    choices = (('DEC', 'Ordem Decrescente'),
               ('CRE', 'Ordem Crescente'),)

    order_by_mapping = {
        'DEC': ['-ano', '-data'],
        'CRE': ['ano', 'data'],
    }

    def __init__(self, *args, **kwargs):
        kwargs['choices'] = self.choices
        super(AnoNumeroOrderingFilter, self).__init__(*args, **kwargs)

    def filter(self, qs, value):
        _value = self.order_by_mapping[value[0]] if value else value
        return super().filter(qs, _value)

class DocumentoAdministrativoOrderingFilter(django_filters.OrderingFilter):

    choices = (('dataPUBd', 'Ordem Decrescente - Data de última atualização'),
               ('dataDOCd', 'Ordem Decrescente - Data, Tipo, Número'),
               ('dataDOCc', 'Ordem Crescente - Data, Tipo, Número'),
               ('dataVENd', 'Ordem Decrescente - Data de Vencimento, Tipo, Número'),
               ('dataVENc', 'Ordem Crescente - Data de Vencimento, Tipo, Número'),)

    order_by_mapping = {
        'dataPUBd': ['-data_ultima_atualizacao', '-id'],
        'dataDOCd': ['-data', '-tipo', '-numero', '-id'],
        'dataDOCc': ['data', 'tipo', 'numero', '-id'],
        'dataVENd': ['-data_vencimento', '-tipo', '-numero', '-id'],
        'dataVENc': ['data_vencimento', 'tipo', 'numero', '-id'],
    }

    def __init__(self, *args, **kwargs):
        kwargs['choices'] = self.choices
        super(DocumentoAdministrativoOrderingFilter,
              self).__init__(*args, **kwargs)

    def filter(self, qs, value):
        if value:
            _value = self.order_by_mapping[value[0]] if value else value
        else:
            _value = self.order_by_mapping['dataPUBd']
        return super().filter(qs, _value)

def gerar_hash_arquivo(arquivo, pk, block_size=2 ** 20):

    arquivo = arquivo.replace('media/sapl/', 'media/original__sapl/')
    arquivo = arquivo.replace('media/cmj/', 'media/original__cmj/')

    md5 = hashlib.md5()
    arq = open(arquivo, 'rb')
    while True:
        data = arq.read(block_size)
        if not data:
            break
        md5.update(data)
    return 'P' + md5.hexdigest() + SEPARADOR_HASH_PROPOSICAO + pk

def hash_sha512(arquivo):

    f = open(arquivo, "rb") if isinstance(arquivo, str) else arquivo

    sha512_hash = hashlib.sha512()
    # Read and update hash string value in blocks of 4K
    for byte_block in iter(lambda: f.read(4096), b""):
        sha512_hash.update(byte_block)

    if isinstance(arquivo, str):
        f.close()

    return sha512_hash.hexdigest()

class ChoiceWithoutValidationField(forms.ChoiceField):

    def validate(self, value):
        if self.required and not value:
            raise ValidationError(
                self.error_messages['required'], code='required')

def models_with_gr_for_model(model):
    return list(map(
        lambda x: x.related_model,
        filter(
            lambda obj: obj.is_relation and
            hasattr(obj, 'field') and
            isinstance(obj, GenericRel),

            model._meta.get_fields(include_hidden=True))
    ))

def generic_relations_for_model(model):
    """
    Esta função retorna uma lista de tuplas de dois elementos, onde o primeiro
    elemento é um model qualquer que implementa SaplGenericRelation (SGR), o
    segundo elemento é uma lista de todas as SGR's que pode haver dentro do
    model retornado na primeira posição da tupla.

    Exemplo: No Sapl, o model Parlamentar tem apenas uma SGR para Autor.
                Se no Sapl existisse apenas essa SGR, o resultado dessa função
                seria:
                    [   #Uma Lista de tuplas
                        (   # cada tupla com dois elementos
                            sapl.parlamentares.models.Parlamentar,
                            [<sapl.utils.SaplGenericRelation: autor>]
                        ),
                    ]
    """
    return list(map(
        lambda x: (x,
                   list(filter(
                       lambda field: (
                           isinstance(
                               field, SaplGenericRelation) and
                           field.related_model == model),
                       x._meta.get_fields(include_hidden=True)))),
        models_with_gr_for_model(model)
    ))

def texto_upload_path(instance, filename, subpath='', pk_first=False):
    """
    update materia_materialegislativa set texto_original = regexp_replace(texto_original, '^sapl/public/', '', 'ig');

    O path gerado por essa função leva em conta a pk de instance.
    isso não é possível naturalmente em uma inclusão pois a implementação
    do django framework chama essa função antes do metodo save

    Por outro lado a forma como vinha sendo formada os paths para os arquivos
    são improdutivas e inconsistentes. Exemplo: usava se o valor de __str__
    do model Proposicao que retornava a descrição da proposição, não retorna
    mais, para uma pasta formar o path do texto_original.
    Ora, o resultado do __str__ citado é totalmente impróprio para ser o nome
    de uma pasta.

    Para colocar a pk no path, a solução encontrada foi implementar o método
    save nas classes que possuem atributo do tipo FileField, implementação esta
    que guarda o FileField em uma variável independente e temporária para savar
    o object sem o arquivo e, logo em seguida, salvá-lo novamente com o arquivo
    Ou seja, nas inclusões que já acomparem um arquivo, haverá dois saves,
    um para armazenar toda a informação e recuperar o pk, e outro logo em
    seguida para armazenar o arquivo.
    """

    filename = re.sub('\s', '_', normalize(filename.strip()).lower())

    from sapl.materia.models import Proposicao, MateriaLegislativa, \
        DocumentoAcessorio
    from sapl.protocoloadm.models import DocumentoAdministrativo, \
        DocumentoAcessorioAdministrativo

    from cmj.cerimonial.models import Visita, Visitante

    if isinstance(
        instance, (
            DocumentoAdministrativo,
            Proposicao,
            DocumentoAcessorioAdministrativo,
            DocumentoAcessorio,
            MateriaLegislativa,
            Visita, Visitante
        )
    ):
        prefix = 'private'
    else:
        prefix = 'public'

    str_path = ('./sapl/%(prefix)s/%(model_name)s/'
                '%(subpath)s/%(pk)s/%(filename)s')

    if pk_first:
        str_path = ('./sapl/%(prefix)s/%(model_name)s/'
                    '%(pk)s/%(subpath)s/%(filename)s')

    if subpath is None:
        subpath = '_'

    path = str_path % \
        {
            'prefix': prefix,
            'model_name': instance._meta.model_name,
            'pk': instance.pk,
            'subpath': subpath,
            'filename': filename
        }

    return path

def qs_override_django_filter(self):
    if not hasattr(self, '_qs'):
        valid = self.is_bound and self.form.is_valid()

        if self.is_bound and not valid:
            """if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
                raise forms.ValidationError(self.form.errors)
            elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:"""
            self._qs = self.queryset.none()
            return self._qs
            # else STRICTNESS.IGNORE...  ignoring

        # start with all the results and filter from there
        qs = self.queryset.all()
        for name, filter_ in six.iteritems(self.filters):
            value = None
            if valid:
                value = self.form.cleaned_data[name]
            else:
                raw_value = self.form[name].value()
                try:
                    value = self.form.fields[name].clean(raw_value)
                except forms.ValidationError:
                    """if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
                        raise
                    elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:"""
                    self._qs = self.queryset.none()
                    return self._qs
                    # else STRICTNESS.IGNORE...  ignoring

            if value is not None:  # valid & clean data
                qs = qs._next_is_sticky()
                qs = filter_.filter(qs, value)

        self._qs = qs

    return self._qs

def filiacao_data(parlamentar, data_inicio, data_fim=None):
    from sapl.parlamentares.models import Filiacao

    filiacoes_parlamentar = Filiacao.objects.filter(
        parlamentar=parlamentar)

    filiacoes = filiacoes_parlamentar.filter(Q(
        data__lte=data_inicio,
        data_desfiliacao__isnull=True) | Q(
        data__lte=data_inicio,
        data_desfiliacao__gte=data_inicio))

    if data_fim:
        filiacoes = filiacoes | filiacoes_parlamentar.filter(
            data__gte=data_inicio,
            data__lte=data_fim)

    data_referencia = data_fim if data_fim else data_inicio

    return ' | '.join([f.nome_partido_ano(data_referencia.year).sigla for f in filiacoes])

def parlamentares_ativos(data_inicio, data_fim=None):
    from sapl.parlamentares.models import Mandato, Parlamentar
    '''
    :param data_inicio: define a data de inicial do período desejado
    :param data_fim: define a data final do período desejado
    :return: queryset dos parlamentares ativos naquele período
    '''
    mandatos_ativos = Mandato.objects.filter(Q(
        data_inicio_mandato__lte=data_inicio,
        data_fim_mandato__isnull=True) | Q(
        data_inicio_mandato__lte=data_inicio,
        data_fim_mandato__gte=data_inicio))
    if data_fim:
        mandatos_ativos = mandatos_ativos | Mandato.objects.filter(
            data_inicio_mandato__gte=data_inicio,
            data_inicio_mandato__lte=data_fim)
    else:
        mandatos_ativos = mandatos_ativos | Mandato.objects.filter(
            data_inicio_mandato__gte=data_inicio)

    parlamentares_id = mandatos_ativos.values_list(
        'parlamentar_id',
        flat=True).distinct('parlamentar_id')

    return Parlamentar.objects.filter(id__in=parlamentares_id)

def show_results_filter_set(qr):
    query_params = set(qr.keys())
    if ((len(query_params) == 1 and 'iframe' in query_params) or
            len(query_params) == 0):
        return False

    return True

def sort_lista_chave(lista, chave):
    """
    :param lista: Uma list a ser ordenada .
    :param chave: Algum atributo (chave) que está presente na lista e qual
    deve ser usado para a ordenação da nova
    lista.
    :return: A lista ordenada pela chave passada.
    """
    lista_ordenada = sorted(lista, key=itemgetter(chave))
    return lista_ordenada

def get_mime_type_from_file_extension(filename):
    ext = filename.split('.')[-1]
    if ext == 'odt':
        mime = 'application/vnd.oasis.opendocument.text'
    else:
        mime = "application/%s" % (ext,)
    return mime

def ExtraiTag(texto, posicao):
    for i in range(posicao, len(texto)):
        if (texto[i] == '>'):
            return i + 1

def TrocaTag(texto, startTag, endTag, sizeStart, sizeEnd, styleName, subinitiTag, subendTag):
    textoSaida = ''
    insideTag = 0
    i = 0
    if texto is None or texto.strip() == '':
        return texto
    if '
' in texto:
        texto = texto.replace('
', '')
        texto = texto.replace('
', '')
    if '
' in texto:
        texto = texto.replace('
', '')
        texto = texto.replace('
', '')
    while (i < len(texto)):
        shard = texto[i:i + sizeStart]
        if (shard == startTag):
            i = ExtraiTag(texto, i)
            textoSaida += subinitiTag + styleName + '">'
            insideTag = 1
        else:
            if (insideTag == 1):
                if (texto[i:i + sizeEnd] == endTag):
                    textoSaida += subendTag
                    insideTag = 0
                    i += sizeEnd
                else:
                    textoSaida += texto[i]
                    i += 1
            else:
                textoSaida += texto[i]
                i += 1

    return textoSaida

def RemoveTag(texto):
    textoSaida = ''
    i = 0

    while (i < len(texto)):

        if (texto[i] == '<'):
            i = ExtraiTag(texto, i)

        else:
            textoSaida += texto[i]
            i += 1

    return textoSaida

def remover_acentos(string):
    return unicodedata.normalize('NFKD', string).encode('ASCII', 'ignore').decode()

def mail_service_configured(request=None):

    logger = logging.getLogger(__name__)

    # if settings.DEBUG:
    #    return False
    if settings.EMAIL_RUNNING is None:
        result = True
        try:
            connection = get_connection()
            connection.open()
        except Exception as e:
            logger.error(str(e))
            result = False
        finally:
            connection.close()
        settings.EMAIL_RUNNING = result

    return settings.EMAIL_RUNNING

def lista_anexados(principal, isMateriaLegislativa=True):
    anexados_total = []
    if isMateriaLegislativa:  # MateriaLegislativa
        from sapl.materia.models import Anexada
        anexados_iterator = Anexada.objects.filter(materia_principal=principal)
    else:  # DocAdm
        from sapl.protocoloadm.models import Anexado
        anexados_iterator = Anexado.objects.filter(
            documento_principal=principal)

    anexadas_temp = list(anexados_iterator)

    while anexadas_temp:
        anx = anexadas_temp.pop()
        if isMateriaLegislativa:
            if anx.materia_anexada not in anexados_total:
                anexados_total.append(anx.materia_anexada)
                anexados_anexado = Anexada.objects.filter(
                    materia_principal=anx.materia_anexada)
                anexadas_temp.extend(anexados_anexado)
        else:
            if anx.documento_anexado not in anexados_total:
                anexados_total.append(anx.documento_anexado)
                anexados_anexado = Anexado.objects.filter(
                    documento_principal=anx.documento_anexado)
                anexadas_temp.extend(anexados_anexado)
    if principal in anexados_total:
        anexados_total.remove(principal)
    return anexados_total

def verifica_afastamento_parlamentar(parlamentar, data_inicio, data_fim=None):
    from sapl.parlamentares.models import AfastamentoParlamentar
    if data_fim:
        existe_afastamento = AfastamentoParlamentar.objects.filter(Q(parlamentar=parlamentar) &
                                                                   ((Q(data_inicio__lte=data_inicio) &
                                                                     Q(data_fim__gte=data_fim)) |
                                                                    (Q(data_inicio__lte=data_inicio) &
                                                                       Q(data_fim__isnull=True))
                                                                    )).exists()
    else:
        existe_afastamento = AfastamentoParlamentar.objects.filter(parlamentar=parlamentar,
                                                                   data_inicio__lte=data_inicio,
                                                                   data_fim__gte=data_inicio).exists()

    return existe_afastamento

def from_date_to_datetime_utc(data):
    """

    :param data: datetime.date
    :return: datetime.timestamp com UTC
    """
    import pytz
    from datetime import datetime

    # from date to datetime
    dt_unware = datetime.combine(data, datetime.min.time())
    dt_utc = pytz.utc.localize(dt_unware)
    return dt_utc

def gerar_pdf_impressos(request, context, template_name):
    template = loader.get_template(template_name)
    html = template.render(context, request)
    pdf = weasyprint.HTML(
        string=html, base_url=request.build_absolute_uri()).write_pdf()

    response = HttpResponse(pdf, content_type='application/pdf')
    response['Content-Disposition'] = 'inline; filename="relatorio_impressos.pdf"'
    response['Content-Transfer-Encoding'] = 'binary'

    return response


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

# Database filename
DATABASE = 'quiz_game.db'

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

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

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

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

        self.topic_entry.focus_set()

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

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

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

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

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

        self.question_entry.focus_set()

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

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

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

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

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

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

        self.answer_entry.focus_set()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        edit_window.wait_window(dialog)

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

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

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

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

    # Populate the tree with data
    refresh_tree()

    return edit_window


# -*- coding: utf-8 -*-
from flask import Blueprint, jsonify, request, Response, stream_with_context
from app.models.promos import Promos
from app import errors, logger
import datetime

mod = Blueprint('promos',__name__)

@mod.route('/')
def main():
    """ Service information endpoint
    """
    return jsonify({
        'service' : 'ByPrice Promos v1',
        'author' : 'ByPrice Dev Team',
        'date' : datetime.datetime.utcnow()
    })

@mod.route('/daily', methods=['GET'])
def generate_promos_by_day_stream():
    """
		Return the promos applying that day, limited by the parameters
		Params:
		{
			'day' : '2017-09-20'
			'num_promos' : 20
		}
	"""
    logger.info("Promos daily stream endpoint!")
    if not request.args:
        raise errors.AppError(80002,"No params in request")
    if 'day' not in request.args:
        raise errors.AppError(80004, "Day params missing")
    else:
        try:
            datetime.datetime.strptime(request.args['day'], '%Y-%m-%d')
        except:
            raise errors.AppError(80012, "Incorrect data format, should be YYYY-MM-DD")
    if 'num_promos' not in request.args or int(request.args['num_promos']) < 1:
        num_promos = 0
    else:
        num_promos = int(request.args['num_promos'])

    def generate():
        return Promos.get_cassandra_promos_by_day(request.args['day'], num_promos)
    return Response(stream_with_context(generate()), content_type='application/json')


import re

class Ingredient(object):
    _parser = re.compile(
        r'(.+): capacity (-?\d+), durability (-?\d+), flavor (-?\d+), texture (-?\d+), calories (-?\d+)')

    def __init__(self, s):
        match = Ingredient._parser.match(s)
        self.name = match.group(1)
        self.capacity = match.group(2)
        self.durability = match.group(3)
        self.flavor = match.group(4)
        self.texture = match.group(5)
        self.calories = match.group(6)

    def __str__(self):
        return 'Ingredient: ' + self.__dict__.__str__()

with open('input_15.txt') as ingredients:
    for i in ingredients:
        print(Ingredient(i))


"""

>>> written(42)
'fourty-two'

>>> written(1)
'one'

>>> written(99)
'ninety-nine'

"""

def written(num:int):
    if num == 42:
        print(str("'fourty-two'"))
    elif num == 1:
        print(str("'one'"))
    else:
        num == 99
        print(str("'ninety-nine'"))


# only use once
import os
import json
from config import path

source_dir = path.BONDS_BY_DEALER_DATE_DOC_DIR
dealer_indices = os.listdir(source_dir)
len_indices = len(dealer_indices)

for i, dealer_index in enumerate(dealer_indices):
    if i % 2 == 0:
        progress = float(i + 1) / len_indices * 100.
        print('\rprogress: %.2f%% ' % progress, end='')

    sub_source_dir = os.path.join(source_dir, dealer_index)
    for file_name in os.listdir(sub_source_dir):
        file_path = os.path.join(sub_source_dir, file_name)

        with open(file_path, 'rb') as f:
            data = json.load(f)

        if not data:
            os.remove(file_path)
            # print(file_path)

print('\ndone')