import asyncio
import enum
import os.path
import time
import sys
import platform
import queue
import traceback
import os
import webbrowser
from decimal import Decimal
from functools import partial, lru_cache, wraps
from typing import (NamedTuple, Callable, Optional, TYPE_CHECKING, Union, List, Dict, Any,
                    Sequence, Iterable, Tuple, Type)

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtGui import (QFont, QColor, QCursor, QPixmap, QStandardItem, QImage,
                         QPalette, QIcon, QFontMetrics, QShowEvent, QPainter, QHelpEvent, QMouseEvent,
                         QContextMenuEvent)
from PyQt5.QtCore import (Qt, QPersistentModelIndex, QModelIndex, pyqtSignal,
                          QCoreApplication, QItemSelectionModel, QThread,
                          QSortFilterProxyModel, QSize, QLocale, QAbstractItemModel,
                          QEvent, QRect, QPoint, QObject)
from PyQt5.QtWidgets import (QPushButton, QLabel, QMessageBox, QHBoxLayout,
                             QAbstractItemView, QVBoxLayout, QLineEdit,
                             QStyle, QDialog, QGroupBox, QButtonGroup, QRadioButton,
                             QFileDialog, QWidget, QToolButton, QTreeView, QPlainTextEdit,
                             QHeaderView, QApplication, QToolTip, QTreeWidget, QStyledItemDelegate,
                             QMenu, QStyleOptionViewItem, QLayout, QLayoutItem, QAbstractButton,
                             QGraphicsEffect, QGraphicsScene, QGraphicsPixmapItem, QSizePolicy)

from electrum.i18n import _, languages
from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, resource_path
from electrum.util import EventListener, event_listener, get_logger
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, PR_BROADCASTING, PR_BROADCAST
from electrum.logging import Logger
from electrum.qrreader import MissingQrDetectionLib

if TYPE_CHECKING:
    from .main_window import ElectrumWindow
    from .paytoedit import PayToEdit

    from electrum.simple_config import SimpleConfig
    from electrum.simple_config import ConfigVarWithConfig

if platform.system() == 'Windows':
    MONOSPACE_FONT = 'Lucida Console'
elif platform.system() == 'Darwin':
    MONOSPACE_FONT = 'Monaco'
else:
    MONOSPACE_FONT = 'monospace'

_logger = get_logger(__name__)

dialogs = []

pr_icons = {
    PR_UNKNOWN:"warning.png",
    PR_UNPAID:"unpaid.png",
    PR_PAID:"confirmed.png",
    PR_EXPIRED:"expired.png",
    PR_INFLIGHT:"unconfirmed.png",
    PR_FAILED:"warning.png",
    PR_ROUTING:"unconfirmed.png",
    PR_UNCONFIRMED:"unconfirmed.png",
    PR_BROADCASTING:"unconfirmed.png",
    PR_BROADCAST:"unconfirmed.png",
}

# filter tx files in QFileDialog:
TRANSACTION_FILE_EXTENSION_FILTER_ANY = "Transaction (*.txn *.psbt);;All files (*)"
TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX = "Partial Transaction (*.psbt)"
TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX = "Complete Transaction (*.txn)"
TRANSACTION_FILE_EXTENSION_FILTER_SEPARATE = (f"{TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX};;"
                                              f"{TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX};;"
                                              f"All files (*)")

class EnterButton(QPushButton):
    def __init__(self, text, func):
        QPushButton.__init__(self, text)
        self.func = func
        self.clicked.connect(func)
        self._orig_text = text

    def keyPressEvent(self, e):
        if e.key() in [Qt.Key_Return, Qt.Key_Enter]:
            self.func()

    def restore_original_text(self):
        self.setText(self._orig_text)

class ThreadedButton(QPushButton):
    def __init__(self, text, task, on_success=None, on_error=None):
        QPushButton.__init__(self, text)
        self.task = task
        self.on_success = on_success
        self.on_error = on_error
        self.clicked.connect(self.run_task)

    def run_task(self):
        self.setEnabled(False)
        self.thread = TaskThread(self)
        self.thread.add(self.task, self.on_success, self.done, self.on_error)

    def done(self):
        self.setEnabled(True)
        self.thread.stop()

class WWLabel(QLabel):
    def __init__ (self, text="", parent=None):
        QLabel.__init__(self, text, parent)
        self.setWordWrap(True)
        self.setTextInteractionFlags(Qt.TextSelectableByMouse)

class AmountLabel(QLabel):
    def __init__(self, *args, **kwargs):
        QLabel.__init__(self, *args, **kwargs)
        self.setFont(QFont(MONOSPACE_FONT))
        self.setTextInteractionFlags(Qt.TextSelectableByMouse)

class HelpMixin:
    def __init__(self, help_text: str, *, help_title: str = None):
        assert isinstance(self, QWidget), "HelpMixin must be a QWidget instance!"
        self.help_text = help_text
        self._help_title = help_title or _('Help')
        if isinstance(self, QLabel):
            self.setTextInteractionFlags(
                (self.textInteractionFlags() | Qt.TextSelectableByMouse)
                & ~Qt.TextSelectableByKeyboard)

    def show_help(self):
        custom_message_box(
            icon=QMessageBox.Information,
            parent=self,
            title=self._help_title,
            text=self.help_text,
            rich_text=True,
        )

class HelpLabel(HelpMixin, QLabel):

    def __init__(self, text: str, help_text: str):
        QLabel.__init__(self, text)
        HelpMixin.__init__(self, help_text)
        self.app = QCoreApplication.instance()
        self.font = self.font()

    @classmethod
    def from_configvar(cls, cv: 'ConfigVarWithConfig') -> 'HelpLabel':
        return HelpLabel(cv.get_short_desc() + ':', cv.get_long_desc())

    def mouseReleaseEvent(self, x):
        self.show_help()

    def enterEvent(self, event):
        self.font.setUnderline(True)
        self.setFont(self.font)
        self.app.setOverrideCursor(QCursor(Qt.PointingHandCursor))
        return QLabel.enterEvent(self, event)

    def leaveEvent(self, event):
        self.font.setUnderline(False)
        self.setFont(self.font)
        self.app.setOverrideCursor(QCursor(Qt.ArrowCursor))
        return QLabel.leaveEvent(self, event)

class HelpButton(HelpMixin, QToolButton):
    def __init__(self, text: str):
        QToolButton.__init__(self)
        HelpMixin.__init__(self, text)
        self.setText('?')
        self.setFocusPolicy(Qt.NoFocus)
        self.setFixedWidth(round(2.2 * char_width_in_lineedit()))
        self.clicked.connect(self.show_help)

class InfoButton(HelpMixin, QPushButton):
    def __init__(self, text: str):
        QPushButton.__init__(self, 'Info')
        HelpMixin.__init__(self, text, help_title=_('Info'))
        self.setFocusPolicy(Qt.NoFocus)
        self.setFixedWidth(6 * char_width_in_lineedit())
        self.clicked.connect(self.show_help)

class Buttons(QHBoxLayout):
    def __init__(self, *buttons):
        QHBoxLayout.__init__(self)
        self.addStretch(1)
        for b in buttons:
            if b is None:
                continue
            self.addWidget(b)

class CloseButton(QPushButton):
    def __init__(self, dialog):
        QPushButton.__init__(self, _("Close"))
        self.clicked.connect(dialog.close)
        self.setDefault(True)

class CopyButton(QPushButton):
    def __init__(self, text_getter, app):
        QPushButton.__init__(self, _("Copy"))
        self.clicked.connect(lambda: app.clipboard().setText(text_getter()))

class CopyCloseButton(QPushButton):
    def __init__(self, text_getter, app, dialog):
        QPushButton.__init__(self, _("Copy and Close"))
        self.clicked.connect(lambda: app.clipboard().setText(text_getter()))
        self.clicked.connect(dialog.close)
        self.setDefault(True)

class OkButton(QPushButton):
    def __init__(self, dialog, label=None):
        QPushButton.__init__(self, label or _("OK"))
        self.clicked.connect(dialog.accept)
        self.setDefault(True)

class CancelButton(QPushButton):
    def __init__(self, dialog, label=None):
        QPushButton.__init__(self, label or _("Cancel"))
        self.clicked.connect(dialog.reject)

class MessageBoxMixin(object):
    def top_level_window_recurse(self, window=None, test_func=None):
        window = window or self
        classes = (WindowModalDialog, QMessageBox)
        if test_func is None:
            test_func = lambda x: True
        for n, child in enumerate(window.children()):
            # Test for visibility as old closed dialogs may not be GC-ed.
            # Only accept children that confirm to test_func.
            if isinstance(child, classes) and child.isVisible() \
                    and test_func(child):
                return self.top_level_window_recurse(child, test_func=test_func)
        return window

    def top_level_window(self, test_func=None):
        return self.top_level_window_recurse(test_func)

    def question(self, msg, parent=None, title=None, icon=None, **kwargs) -> bool:
        Yes, No = QMessageBox.Yes, QMessageBox.No
        return Yes == self.msg_box(icon=icon or QMessageBox.Question,
                                   parent=parent,
                                   title=title or '',
                                   text=msg,
                                   buttons=Yes|No,
                                   defaultButton=No,
                                   **kwargs)

    def show_warning(self, msg, parent=None, title=None, **kwargs):
        return self.msg_box(QMessageBox.Warning, parent,
                            title or _('Warning'), msg, **kwargs)

    def show_error(self, msg, parent=None, **kwargs):
        return self.msg_box(QMessageBox.Warning, parent,
                            _('Error'), msg, **kwargs)

    def show_critical(self, msg, parent=None, title=None, **kwargs):
        return self.msg_box(QMessageBox.Critical, parent,
                            title or _('Critical Error'), msg, **kwargs)

    def show_message(self, msg, parent=None, title=None, **kwargs):
        return self.msg_box(QMessageBox.Information, parent,
                            title or _('Information'), msg, **kwargs)

    def msg_box(self, icon, parent, title, text, *, buttons=QMessageBox.Ok,
                defaultButton=QMessageBox.NoButton, rich_text=False,
                checkbox=None):
        parent = parent or self.top_level_window()
        return custom_message_box(icon=icon,
                                  parent=parent,
                                  title=title,
                                  text=text,
                                  buttons=buttons,
                                  defaultButton=defaultButton,
                                  rich_text=rich_text,
                                  checkbox=checkbox)

def custom_message_box(*, icon, parent, title, text, buttons=QMessageBox.Ok,
                       defaultButton=QMessageBox.NoButton, rich_text=False,
                       checkbox=None):
    if type(icon) is QPixmap:
        d = QMessageBox(QMessageBox.Information, title, str(text), buttons, parent)
        d.setIconPixmap(icon)
    else:
        d = QMessageBox(icon, title, str(text), buttons, parent)
    d.setWindowModality(Qt.WindowModal)
    d.setDefaultButton(defaultButton)
    if rich_text:
        d.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.LinksAccessibleByMouse)
        # set AutoText instead of RichText
        # AutoText lets Qt figure out whether to render as rich text.
        # e.g. if text is actually plain text and uses "\n" newlines;
        #      and we set RichText here, newlines would be swallowed
        d.setTextFormat(Qt.AutoText)
    else:
        d.setTextInteractionFlags(Qt.TextSelectableByMouse)
        d.setTextFormat(Qt.PlainText)
    if checkbox is not None:
        d.setCheckBox(checkbox)
    return d.exec_()

class WindowModalDialog(QDialog, MessageBoxMixin):
    '''Handy wrapper; window modal dialogs are better for our multi-window
    daemon model as other wallet windows can still be accessed.'''
    def __init__(self, parent, title=None):
        QDialog.__init__(self, parent)
        self.setWindowModality(Qt.WindowModal)
        if title:
            self.setWindowTitle(title)

class WaitingDialog(WindowModalDialog):
    '''Shows a please wait dialog whilst running a task.  It is not
    necessary to maintain a reference to this dialog.'''
    def __init__(self, parent: QWidget, message: str, task, on_success=None, on_error=None, on_cancel=None):
        assert parent
        if isinstance(parent, MessageBoxMixin):
            parent = parent.top_level_window()
        WindowModalDialog.__init__(self, parent, _("Please wait"))
        self.message_label = QLabel(message)
        vbox = QVBoxLayout(self)
        vbox.addWidget(self.message_label)
        if on_cancel:
            self.cancel_button = CancelButton(self)
            self.cancel_button.clicked.connect(on_cancel)
            vbox.addLayout(Buttons(self.cancel_button))
        self.accepted.connect(self.on_accepted)
        self.show()
        self.thread = TaskThread(self)
        self.thread.finished.connect(self.deleteLater)  # see #3956
        self.thread.add(task, on_success, self.accept, on_error)

    def wait(self):
        self.thread.wait()

    def on_accepted(self):
        self.thread.stop()

    def update(self, msg):
        print(msg)
        self.message_label.setText(msg)

class BlockingWaitingDialog(WindowModalDialog):
    """Shows a waiting dialog whilst running a task.
    Should be called from the GUI thread. The GUI thread will be blocked while
    the task is running; the point of the dialog is to provide feedback
    to the user regarding what is going on.
    """
    def __init__(self, parent: QWidget, message: str, task: Callable[[], Any]):
        assert parent
        if isinstance(parent, MessageBoxMixin):
            parent = parent.top_level_window()
        WindowModalDialog.__init__(self, parent, _("Please wait"))
        self.message_label = QLabel(message)
        vbox = QVBoxLayout(self)
        vbox.addWidget(self.message_label)
        self.finished.connect(self.deleteLater)  # see #3956
        # show popup
        self.show()
        # refresh GUI; needed for popup to appear and for message_label to get drawn
        QCoreApplication.processEvents()
        QCoreApplication.processEvents()
        try:
            # block and run given task
            task()
        finally:
            # close popup
            self.accept()

def line_dialog(parent, title, label, ok_label, default=None):
    dialog = WindowModalDialog(parent, title)
    dialog.setMinimumWidth(500)
    l = QVBoxLayout()
    dialog.setLayout(l)
    l.addWidget(QLabel(label))
    txt = QLineEdit()
    if default:
        txt.setText(default)
    l.addWidget(txt)
    l.addLayout(Buttons(CancelButton(dialog), OkButton(dialog, ok_label)))
    if dialog.exec_():
        return txt.text()

def text_dialog(
        *,
        parent,
        title,
        header_layout,
        ok_label,
        default=None,
        allow_multi=False,
        config: 'SimpleConfig',
):
    from .qrtextedit import ScanQRTextEdit
    dialog = WindowModalDialog(parent, title)
    dialog.setMinimumWidth(600)
    l = QVBoxLayout()
    dialog.setLayout(l)
    if isinstance(header_layout, str):
        l.addWidget(QLabel(header_layout))
    else:
        l.addLayout(header_layout)
    txt = ScanQRTextEdit(allow_multi=allow_multi, config=config)
    if default:
        txt.setText(default)
    l.addWidget(txt)
    l.addLayout(Buttons(CancelButton(dialog), OkButton(dialog, ok_label)))
    if dialog.exec_():
        return txt.toPlainText()

class ChoicesLayout(object):
    def __init__(self, msg, choices, on_clicked=None, checked_index=0):
        vbox = QVBoxLayout()
        if len(msg) > 50:
            vbox.addWidget(WWLabel(msg))
            msg = ""
        gb2 = QGroupBox(msg)
        vbox.addWidget(gb2)
        vbox2 = QVBoxLayout()
        gb2.setLayout(vbox2)
        self.group = group = QButtonGroup()
        if isinstance(choices, list):
            iterator = enumerate(choices)
        else:
            iterator = choices.items()
        for i, c in iterator:
            button = QRadioButton(gb2)
            button.setText(c)
            vbox2.addWidget(button)
            group.addButton(button)
            group.setId(button, i)
            if i == checked_index:
                button.setChecked(True)
        if on_clicked:
            group.buttonClicked.connect(partial(on_clicked, self))
        self.vbox = vbox

    def layout(self):
        return self.vbox

    def selected_index(self):
        return self.group.checkedId()

class ChoiceWidget(QWidget):
    itemSelected = pyqtSignal([int], arguments=['index'])

    def __init__(self, *, message=None, choices=None, selected=None):
        QWidget.__init__(self)
        vbox = QVBoxLayout()
        self.setLayout(vbox)

        if choices is None:
            choices = []

        self.selected_index = -1
        self.selected_item = None
        self.choices = choices

        if message and len(message) > 50:
            vbox.addWidget(WWLabel(message))
            message = ""
        gb2 = QGroupBox(message)
        vbox.addWidget(gb2)
        vbox2 = QVBoxLayout()
        gb2.setLayout(vbox2)
        self.group = group = QButtonGroup()
        assert isinstance(choices, list)
        iterator = enumerate(choices)
        for i, c in iterator:
            button = QRadioButton(gb2)
            button.setText(c[1])
            vbox2.addWidget(button)
            group.addButton(button)
            group.setId(button, i)
            if (i == 0 and selected is None) or c[0] == selected:
                self.selected_index = i
                self.selected_item = c
                button.setChecked(True)
        group.buttonClicked.connect(self.on_selected)

    def on_selected(self, button):
        self.selected_index = self.group.id(button)
        self.selected_item = self.choices[self.selected_index]
        self.itemSelected.emit(self.selected_index)

def address_field(addresses):
    hbox = QHBoxLayout()
    address_e = QLineEdit()
    if addresses and len(addresses) > 0:
        address_e.setText(addresses[0])
    else:
        addresses = []
    def func():
        try:
            i = addresses.index(str(address_e.text())) + 1
            i = i % len(addresses)
            address_e.setText(addresses[i])
        except ValueError:
            # the user might have changed address_e to an
            # address not in the wallet (or to something that isn't an address)
            if addresses and len(addresses) > 0:
                address_e.setText(addresses[0])
    button = QPushButton(_('Address'))
    button.clicked.connect(func)
    hbox.addWidget(button)
    hbox.addWidget(address_e)
    return hbox, address_e

def filename_field(parent, config, defaultname, select_msg):

    vbox = QVBoxLayout()
    vbox.addWidget(QLabel(_("Format")))
    gb = QGroupBox("format", parent)
    b1 = QRadioButton(gb)
    b1.setText(_("CSV"))
    b1.setChecked(True)
    b2 = QRadioButton(gb)
    b2.setText(_("json"))
    vbox.addWidget(b1)
    vbox.addWidget(b2)

    hbox = QHBoxLayout()

    directory = config.IO_DIRECTORY
    path = os.path.join(directory, defaultname)
    filename_e = QLineEdit()
    filename_e.setText(path)

    def func():
        text = filename_e.text()
        _filter = "*.csv" if defaultname.endswith(".csv") else "*.json" if defaultname.endswith(".json") else None
        p = getSaveFileName(
            parent=None,
            title=select_msg,
            filename=text,
            filter=_filter,
            config=config,
        )
        if p:
            filename_e.setText(p)

    button = QPushButton(_('File'))
    button.clicked.connect(func)
    hbox.addWidget(button)
    hbox.addWidget(filename_e)
    vbox.addLayout(hbox)

    def set_csv(v):
        text = filename_e.text()
        text = text.replace(".json",".csv") if v else text.replace(".csv",".json")
        filename_e.setText(text)

    b1.clicked.connect(lambda: set_csv(True))
    b2.clicked.connect(lambda: set_csv(False))

    return vbox, filename_e, b1

def get_iconname_qrcode() -> str:
    return "qrcode_white.png" if ColorScheme.dark_scheme else "qrcode.png"

def get_iconname_camera() -> str:
    return "camera_white.png" if ColorScheme.dark_scheme else "camera_dark.png"

def editor_contextMenuEvent(self, p: 'PayToEdit', e: 'QContextMenuEvent') -> None:
    m = self.createStandardContextMenu()
    m.addSeparator()
    m.addAction(read_QIcon(get_iconname_camera()),    _("Read QR code with camera"), p.on_qr_from_camera_input_btn)
    m.addAction(read_QIcon("picture_in_picture.png"), _("Read QR code from screen"), p.on_qr_from_screenshot_input_btn)
    m.addAction(read_QIcon("file.png"), _("Read file"), p.on_input_file)
    m.exec_(e.globalPos())

class GenericInputHandler:

    def input_qr_from_camera(
            self,
            *,
            config: 'SimpleConfig',
            allow_multi: bool = False,
            show_error: Callable[[str], None],
            setText: Callable[[str], None] = None,
            parent: QWidget = None,
    ) -> None:
        if setText is None:
            setText = self.setText
        def cb(success: bool, error: str, data):
            if not success:
                if error:
                    show_error(error)
                return
            if not data:
                data = ''
            if allow_multi:
                new_text = self.text() + data + '\n'
            else:
                new_text = data
                try:
                    setText(new_text)
                except Exception as e:
                    show_error(_('Invalid payment identifier in QR') + ':\n' + repr(e))

        from .qrreader import scan_qrcode
        if parent is None:
            parent = self if isinstance(self, QWidget) else None
        scan_qrcode(parent=parent, config=config, callback=cb)

    def input_qr_from_screenshot(
            self,
            *,
            allow_multi: bool = False,
            show_error: Callable[[str], None],
            setText: Callable[[str], None] = None,
    ) -> None:
        if setText is None:
            setText = self.setText
        from .qrreader import scan_qr_from_image
        screenshots = [screen.grabWindow(0).toImage()
                       for screen in QApplication.instance().screens()]
        if all(screen.allGray() for screen in screenshots):
            show_error(_("Failed to take screenshot."))
            return
        scanned_qr = None
        for screenshot in screenshots:
            try:
                scan_result = scan_qr_from_image(screenshot)
            except MissingQrDetectionLib as e:
                show_error(_("Unable to scan image.") + "\n" + repr(e))
                return
            if len(scan_result) > 0:
                if (scanned_qr is not None) or len(scan_result) > 1:
                    show_error(_("More than one QR code was found on the screen."))
                    return
                scanned_qr = scan_result
        if scanned_qr is None:
            show_error(_("No QR code was found on the screen."))
            return
        data = scanned_qr[0].data
        if allow_multi:
            new_text = self.text() + data + '\n'
        else:
            new_text = data
            try:
                setText(new_text)
            except Exception as e:
                show_error(_('Invalid payment identifier in QR') + ':\n' + repr(e))

    def input_file(
            self,
            *,
            config: 'SimpleConfig',
            show_error: Callable[[str], None],
            setText: Callable[[str], None] = None,
    ) -> None:
        if setText is None:
            setText = self.setText
        fileName = getOpenFileName(
            parent=None,
            title='select file',
            config=config,
        )
        if not fileName:
            return
        try:
            try:
                with open(fileName, "r") as f:
                    data = f.read()
            except UnicodeError as e:
                with open(fileName, "rb") as f:
                    data = f.read()
                data = data.hex()
        except BaseException as e:
            show_error(_('Error opening file') + ':\n' + repr(e))
        else:
            try:
                setText(data)
            except Exception as e:
                show_error(_('Invalid payment identifier in file') + ':\n' + repr(e))

    def input_paste_from_clipboard(
            self,
            *,
            setText: Callable[[str], None] = None,
    ) -> None:
        if setText is None:
            setText = self.setText
        app = QApplication.instance()
        setText(app.clipboard().text())

class OverlayControlMixin(GenericInputHandler):
    STYLE_SHEET_COMMON = '''
    QPushButton { border-width: 1px; padding: 0px; margin: 0px; }
    '''

    STYLE_SHEET_LIGHT = '''
    QPushButton { border: 1px solid transparent; }
    QPushButton:hover { border: 1px solid #3daee9; }
    '''

    def __init__(self, middle: bool = False):
        GenericInputHandler.__init__(self)
        assert isinstance(self, QWidget)
        assert isinstance(self, OverlayControlMixin)  # only here for type-hints in IDE
        self.middle = middle
        self.overlay_widget = QWidget(self)
        style_sheet = self.STYLE_SHEET_COMMON
        if not ColorScheme.dark_scheme:
            style_sheet = style_sheet + self.STYLE_SHEET_LIGHT
        self.overlay_widget.setStyleSheet(style_sheet)
        self.overlay_layout = QHBoxLayout(self.overlay_widget)
        self.overlay_layout.setContentsMargins(0, 0, 0, 0)
        self.overlay_layout.setSpacing(1)
        self._updateOverlayPos()

    def resizeEvent(self, e):
        super().resizeEvent(e)
        self._updateOverlayPos()

    def _updateOverlayPos(self):
        frame_width = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
        overlay_size = self.overlay_widget.sizeHint()
        x = self.rect().right() - frame_width - overlay_size.width()
        y = self.rect().bottom() - overlay_size.height()
        middle = self.middle
        if hasattr(self, 'document'):
            # Keep the buttons centered if we have less than 2 lines in the editor
            line_spacing = QFontMetrics(self.document().defaultFont()).lineSpacing()
            if self.rect().height() < (line_spacing * 2):
                middle = True
        y = (y / 2) + frame_width if middle else y - frame_width
        if hasattr(self, 'verticalScrollBar') and self.verticalScrollBar().isVisible():
            scrollbar_width = self.style().pixelMetric(QStyle.PM_ScrollBarExtent)
            x -= scrollbar_width
        self.overlay_widget.move(int(x), int(y))

    def addWidget(self, widget: QWidget):
        # The old code positioned the items the other way around, so we just insert at position 0 instead
        self.overlay_layout.insertWidget(0, widget)

    def addButton(self, icon_name: str, on_click, tooltip: str) -> QPushButton:
        button = QPushButton(self.overlay_widget)
        button.setToolTip(tooltip)
        button.setIcon(read_QIcon(icon_name))
        button.setCursor(QCursor(Qt.PointingHandCursor))
        button.clicked.connect(on_click)
        self.addWidget(button)
        return button

    def addCopyButton(self):
        def on_copy():
            app = QApplication.instance()
            app.clipboard().setText(self.text())
            QToolTip.showText(QCursor.pos(), _("Text copied to clipboard"), self)

        self.addButton("copy.png", on_copy, _("Copy to clipboard"))

    def addPasteButton(
            self,
            *,
            setText: Callable[[str], None] = None,
    ):
        input_paste_from_clipboard = partial(
            self.input_paste_from_clipboard,
            setText=setText,
        )
        self.addButton("copy.png", input_paste_from_clipboard, _("Paste from clipboard"))

    def add_qr_show_button(self, *, config: 'SimpleConfig', title: Optional[str] = None):
        if title is None:
            title = _("QR code")

        def qr_show():
            from .qrcodewidget import QRDialog
            try:
                s = str(self.text())
            except Exception:
                s = self.text()
            if not s:
                return
            QRDialog(
                data=s,
                parent=self,
                title=title,
                config=config,
            ).exec_()

        self.addButton(get_iconname_qrcode(), qr_show, _("Show as QR code"))
        # side-effect: we export this method:
        self.on_qr_show_btn = qr_show

    def add_qr_input_combined_button(
            self,
            *,
            config: 'SimpleConfig',
            allow_multi: bool = False,
            show_error: Callable[[str], None],
            setText: Callable[[str], None] = None,
    ):
        input_qr_from_camera = partial(
            self.input_qr_from_camera,
            config=config,
            allow_multi=allow_multi,
            show_error=show_error,
            setText=setText,
        )
        input_qr_from_screenshot = partial(
            self.input_qr_from_screenshot,
            allow_multi=allow_multi,
            show_error=show_error,
            setText=setText,
        )
        self.add_menu_button(
            icon=get_iconname_camera(),
            tooltip=_("Read QR code"),
            options=[
                (get_iconname_camera(),    _("Read QR code from camera"), input_qr_from_camera),
                ("picture_in_picture.png", _("Read QR code from screen"), input_qr_from_screenshot),
            ],
        )
        # side-effect: we export these methods:
        self.on_qr_from_camera_input_btn = input_qr_from_camera
        self.on_qr_from_screenshot_input_btn = input_qr_from_screenshot

    def add_qr_input_from_camera_button(
            self,
            *,
            config: 'SimpleConfig',
            allow_multi: bool = False,
            show_error: Callable[[str], None],
            setText: Callable[[str], None] = None,
    ):
        input_qr_from_camera = partial(
            self.input_qr_from_camera,
            config=config,
            allow_multi=allow_multi,
            show_error=show_error,
            setText=setText,
        )
        self.addButton(get_iconname_camera(), input_qr_from_camera, _("Read QR code from camera"))
        # side-effect: we export these methods:
        self.on_qr_from_camera_input_btn = input_qr_from_camera

    def add_file_input_button(
            self,
            *,
            config: 'SimpleConfig',
            show_error: Callable[[str], None],
            setText: Callable[[str], None] = None,
    ) -> None:
        input_file = partial(
            self.input_file,
            config=config,
            show_error=show_error,
            setText=setText,
        )
        self.addButton("file.png", input_file, _("Read file"))

    def add_menu_button(
            self,
            *,
            options: Sequence[Tuple[Optional[str], str, Callable[[], None]]],  # list of (icon, text, cb)
            icon: Optional[str] = None,
            tooltip: Optional[str] = None,
    ):
        if icon is None:
            icon = "menu_vertical_white.png" if ColorScheme.dark_scheme else "menu_vertical.png"
        if tooltip is None:
            tooltip = _("Other options")
        btn = self.addButton(icon, lambda: None, tooltip)
        menu = QMenu()
        for opt_icon, opt_text, opt_cb in options:
            if opt_icon is None:
                menu.addAction(opt_text, opt_cb)
            else:
                menu.addAction(read_QIcon(opt_icon), opt_text, opt_cb)
        btn.setMenu(menu)

class ButtonsLineEdit(OverlayControlMixin, QLineEdit):
    def __init__(self, text=None):
        QLineEdit.__init__(self, text)
        OverlayControlMixin.__init__(self, middle=True)

class ShowQRLineEdit(ButtonsLineEdit):
    """ read-only line with qr and copy buttons """
    def __init__(self, text: str, config, title=None):
        ButtonsLineEdit.__init__(self, text)
        self.setReadOnly(True)
        self.setFont(QFont(MONOSPACE_FONT))
        self.add_qr_show_button(config=config, title=title)
        self.addCopyButton()

class ButtonsTextEdit(OverlayControlMixin, QPlainTextEdit):
    def __init__(self, text=None):
        QPlainTextEdit.__init__(self, text)
        OverlayControlMixin.__init__(self)
        self.setText = self.setPlainText
        self.text = self.toPlainText

class PasswordLineEdit(QLineEdit):
    def __init__(self, *args, **kwargs):
        QLineEdit.__init__(self, *args, **kwargs)
        self.setEchoMode(QLineEdit.Password)

    def clear(self):
        # Try to actually overwrite the memory.
        # This is really just a best-effort thing...
        self.setText(len(self.text()) * " ")
        super().clear()

class TaskThread(QThread, Logger):
    '''Thread that runs background tasks.  Callbacks are guaranteed
    to happen in the context of its parent.'''

    class Task(NamedTuple):
        task: Callable
        cb_success: Optional[Callable]
        cb_done: Optional[Callable]
        cb_error: Optional[Callable]
        cancel: Optional[Callable] = None

    doneSig = pyqtSignal(object, object, object)

    def __init__(self, parent, on_error=None):
        QThread.__init__(self, parent)
        Logger.__init__(self)
        self.on_error = on_error
        self.tasks = queue.Queue()
        self._cur_task = None  # type: Optional[TaskThread.Task]
        self._stopping = False
        self.doneSig.connect(self.on_done)
        self.start()

    def add(self, task, on_success=None, on_done=None, on_error=None, *, cancel=None):
        if self._stopping:
            self.logger.warning(f"stopping or already stopped but tried to add new task.")
            return
        on_error = on_error or self.on_error
        task_ = TaskThread.Task(task, on_success, on_done, on_error, cancel=cancel)
        self.tasks.put(task_)

    def run(self):
        while True:
            if self._stopping:
                break
            task = self.tasks.get()  # type: TaskThread.Task
            self._cur_task = task
            if not task or self._stopping:
                break
            try:
                result = task.task()
                self.doneSig.emit(result, task.cb_done, task.cb_success)
            except BaseException:
                self.doneSig.emit(sys.exc_info(), task.cb_done, task.cb_error)

    def on_done(self, result, cb_done, cb_result):
        # This runs in the parent's thread.
        if cb_done:
            cb_done()
        if cb_result:
            cb_result(result)

    def stop(self):
        self._stopping = True
        # try to cancel currently running task now.
        # if the task does not implement "cancel", we will have to wait until it finishes.
        task = self._cur_task
        if task and task.cancel:
            task.cancel()
        # cancel the remaining tasks in the queue
        while True:
            try:
                task = self.tasks.get_nowait()
            except queue.Empty:
                break
            if task and task.cancel:
                task.cancel()
        self.tasks.put(None)  # in case the thread is still waiting on the queue
        self.exit()
        self.wait()

class ColorSchemeItem:
    def __init__(self, fg_color, bg_color):
        self.colors = (fg_color, bg_color)

    def _get_color(self, background):
        return self.colors[(int(background) + int(ColorScheme.dark_scheme)) % 2]

    def as_stylesheet(self, background=False):
        css_prefix = "background-" if background else ""
        color = self._get_color(background)
        return "QWidget {{ {}color:{}; }}".format(css_prefix, color)

    def as_color(self, background=False):
        color = self._get_color(background)
        return QColor(color)

class ColorScheme:
    dark_scheme = False

    GREEN = ColorSchemeItem("#117c11", "#8af296")
    YELLOW = ColorSchemeItem("#897b2a", "#ffff00")
    RED = ColorSchemeItem("#7c1111", "#f18c8c")
    BLUE = ColorSchemeItem("#123b7c", "#8cb3f2")
    LIGHTBLUE = ColorSchemeItem("black", "#d0f0ff")
    DEFAULT = ColorSchemeItem("black", "white")
    GRAY = ColorSchemeItem("gray", "gray")

    @staticmethod
    def has_dark_background(widget):
        brightness = sum(widget.palette().color(QPalette.Background).getRgb()[0:3])
        return brightness < (255*3/2)

    @staticmethod
    def update_from_widget(widget, force_dark=False):
        ColorScheme.dark_scheme = bool(force_dark or ColorScheme.has_dark_background(widget))

class AcceptFileDragDrop:
    def __init__(self, file_type=""):
        assert isinstance(self, QWidget)
        self.setAcceptDrops(True)
        self.file_type = file_type

    def validateEvent(self, event):
        if not event.mimeData().hasUrls():
            event.ignore()
            return False
        for url in event.mimeData().urls():
            if not url.toLocalFile().endswith(self.file_type):
                event.ignore()
                return False
        event.accept()
        return True

    def dragEnterEvent(self, event):
        self.validateEvent(event)

    def dragMoveEvent(self, event):
        if self.validateEvent(event):
            event.setDropAction(Qt.CopyAction)

    def dropEvent(self, event):
        if self.validateEvent(event):
            for url in event.mimeData().urls():
                self.onFileAdded(url.toLocalFile())

    def onFileAdded(self, fn):
        raise NotImplementedError()

def import_meta_gui(electrum_window: 'ElectrumWindow', title, importer, on_success):
    filter_ = "JSON (*.json);;All files (*)"
    filename = getOpenFileName(
        parent=electrum_window,
        title=_("Open {} file").format(title),
        filter=filter_,
        config=electrum_window.config,
    )
    if not filename:
        return
    try:
        importer(filename)
    except FileImportFailed as e:
        electrum_window.show_critical(str(e))
    else:
        electrum_window.show_message(_("Your {} were successfully imported").format(title))
        on_success()

def export_meta_gui(electrum_window: 'ElectrumWindow', title, exporter):
    filter_ = "JSON (*.json);;All files (*)"
    filename = getSaveFileName(
        parent=electrum_window,
        title=_("Select file to save your {}").format(title),
        filename='electrum_{}.json'.format(title),
        filter=filter_,
        config=electrum_window.config,
    )
    if not filename:
        return
    try:
        exporter(filename)
    except FileExportFailed as e:
        electrum_window.show_critical(str(e))
    else:
        electrum_window.show_message(_("Your {0} were exported to '{1}'")
                                     .format(title, str(filename)))

def getOpenFileName(*, parent, title, filter="", config: 'SimpleConfig') -> Optional[str]:
    """Custom wrapper for getOpenFileName that remembers the path selected by the user."""
    directory = config.IO_DIRECTORY
    fileName, __ = QFileDialog.getOpenFileName(parent, title, directory, filter)
    if fileName and directory != os.path.dirname(fileName):
        config.IO_DIRECTORY = os.path.dirname(fileName)
    return fileName

def getSaveFileName(
        *,
        parent,
        title,
        filename,
        filter="",
        default_extension: str = None,
        default_filter: str = None,
        config: 'SimpleConfig',
) -> Optional[str]:
    """Custom wrapper for getSaveFileName that remembers the path selected by the user."""
    directory = config.IO_DIRECTORY
    path = os.path.join(directory, filename)

    file_dialog = QFileDialog(parent, title, path, filter)
    file_dialog.setAcceptMode(QFileDialog.AcceptSave)
    if default_extension:
        # note: on MacOS, the selected filter's first extension seems to have priority over this...
        file_dialog.setDefaultSuffix(default_extension)
    if default_filter:
        assert default_filter in filter, f"default_filter={default_filter!r} does not appear in filter={filter!r}"
        file_dialog.selectNameFilter(default_filter)
    if file_dialog.exec() != QDialog.Accepted:
        return None

    selected_path = file_dialog.selectedFiles()[0]
    if selected_path and directory != os.path.dirname(selected_path):
        config.IO_DIRECTORY = os.path.dirname(selected_path)
    return selected_path

def icon_path(icon_basename: str):
    return resource_path('gui', 'icons', icon_basename)

@lru_cache(maxsize=1000)
def read_QIcon(icon_basename: str) -> QIcon:
    return QIcon(icon_path(icon_basename))

class IconLabel(QWidget):
    HorizontalSpacing = 2
    def __init__(self, *, text='', final_stretch=True):
        super(QWidget, self).__init__()
        size = max(16, font_height())
        self.icon_size = QSize(size, size)
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)
        self.icon = QLabel()
        self.label = QLabel(text)
        self.label.setTextInteractionFlags(Qt.TextSelectableByMouse)
        layout.addWidget(self.label)
        layout.addSpacing(self.HorizontalSpacing)
        layout.addWidget(self.icon)
        if final_stretch:
            layout.addStretch()
    def setText(self, text):
        self.label.setText(text)
    def setIcon(self, icon):
        self.icon.setPixmap(icon.pixmap(self.icon_size))
        self.icon.repaint()  # macOS hack for #6269

def char_width_in_lineedit() -> int:
    char_width = QFontMetrics(QLineEdit().font()).averageCharWidth()
    # 'averageCharWidth' seems to underestimate on Windows, hence 'max()'
    return max(9, char_width)

def font_height() -> int:
    return QFontMetrics(QLabel().font()).height()

def webopen(url: str):
    if sys.platform == 'linux' and os.environ.get('APPIMAGE'):
        # When on Linux webbrowser.open can fail in AppImage because it can't find the correct libdbus.
        # We just fork the process and unset LD_LIBRARY_PATH before opening the URL.
        # See #5425
        if os.fork() == 0:
            del os.environ['LD_LIBRARY_PATH']
            webbrowser.open(url)
            os._exit(0)
    else:
        webbrowser.open(url)

class FixedAspectRatioLayout(QLayout):
    def __init__(self, parent: QWidget = None, aspect_ratio: float = 1.0):
        super().__init__(parent)
        self.aspect_ratio = aspect_ratio
        self.items: List[QLayoutItem] = []

    def set_aspect_ratio(self, aspect_ratio: float = 1.0):
        self.aspect_ratio = aspect_ratio
        self.update()

    def addItem(self, item: QLayoutItem):
        self.items.append(item)

    def count(self) -> int:
        return len(self.items)

    def itemAt(self, index: int) -> QLayoutItem:
        if index >= len(self.items):
            return None
        return self.items[index]

    def takeAt(self, index: int) -> QLayoutItem:
        if index >= len(self.items):
            return None
        return self.items.pop(index)

    def _get_contents_margins_size(self) -> QSize:
        margins = self.contentsMargins()
        return QSize(margins.left() + margins.right(), margins.top() + margins.bottom())

    def setGeometry(self, rect: QRect):
        super().setGeometry(rect)
        if not self.items:
            return

        contents = self.contentsRect()
        if contents.height() > 0:
            c_aratio = contents.width() / contents.height()
        else:
            c_aratio = 1
        s_aratio = self.aspect_ratio
        item_rect = QRect(QPoint(0, 0), QSize(
            contents.width() if c_aratio < s_aratio else int(contents.height() * s_aratio),
            contents.height() if c_aratio > s_aratio else int(contents.width() / s_aratio)
        ))

        content_margins = self.contentsMargins()
        free_space = contents.size() - item_rect.size()

        for item in self.items:
            if free_space.width() > 0 and not item.alignment() & Qt.AlignLeft:
                if item.alignment() & Qt.AlignRight:
                    item_rect.moveRight(contents.width() + content_margins.right())
                else:
                    item_rect.moveLeft(content_margins.left() + (free_space.width() // 2))
            else:
                item_rect.moveLeft(content_margins.left())

            if free_space.height() > 0 and not item.alignment() & Qt.AlignTop:
                if item.alignment() & Qt.AlignBottom:
                    item_rect.moveBottom(contents.height() + content_margins.bottom())
                else:
                    item_rect.moveTop(content_margins.top() + (free_space.height() // 2))
            else:
                item_rect.moveTop(content_margins.top())

            item.widget().setGeometry(item_rect)

    def sizeHint(self) -> QSize:
        result = QSize()
        for item in self.items:
            result = result.expandedTo(item.sizeHint())
        return self._get_contents_margins_size() + result

    def minimumSize(self) -> QSize:
        result = QSize()
        for item in self.items:
            result = result.expandedTo(item.minimumSize())
        return self._get_contents_margins_size() + result

    def expandingDirections(self) -> Qt.Orientations:
        return Qt.Horizontal | Qt.Vertical

def QColorLerp(a: QColor, b: QColor, t: float):
    """
    Blends two QColors. t=0 returns a. t=1 returns b. t=0.5 returns evenly mixed.
    """
    t = max(min(t, 1.0), 0.0)
    i_t = 1.0 - t
    return QColor(
        int((a.red()   * i_t) + (b.red()   * t)),
        int((a.green() * i_t) + (b.green() * t)),
        int((a.blue()  * i_t) + (b.blue()  * t)),
        int((a.alpha() * i_t) + (b.alpha() * t)),
    )

class ImageGraphicsEffect(QObject):
    """
    Applies a QGraphicsEffect to a QImage
    """

    def __init__(self, parent: QObject, effect: QGraphicsEffect):
        super().__init__(parent)
        assert effect, 'effect must be set'
        self.effect = effect
        self.graphics_scene = QGraphicsScene()
        self.graphics_item = QGraphicsPixmapItem()
        self.graphics_item.setGraphicsEffect(effect)
        self.graphics_scene.addItem(self.graphics_item)

    def apply(self, image: QImage):
        assert image, 'image must be set'
        result = QImage(image.size(), QImage.Format_ARGB32)
        result.fill(Qt.transparent)
        painter = QPainter(result)
        self.graphics_item.setPixmap(QPixmap.fromImage(image))
        self.graphics_scene.render(painter)
        self.graphics_item.setPixmap(QPixmap())
        return result

class QtEventListener(EventListener):
    qt_callback_signal = QtCore.pyqtSignal(tuple)

    def register_callbacks(self):
        self.qt_callback_signal.connect(self.on_qt_callback_signal)
        EventListener.register_callbacks(self)

    def unregister_callbacks(self):
        try:
            self.qt_callback_signal.disconnect()
        except RuntimeError:  # wrapped Qt object might be deleted
            pass
        EventListener.unregister_callbacks(self)

    def on_qt_callback_signal(self, args):
        func = args[0]
        return func(self, *args[1:])

# decorator for members of the QtEventListener class
def qt_event_listener(func):
    func = event_listener(func)
    @wraps(func)
    def decorator(self, *args):
        self.qt_callback_signal.emit( (func,) + args)
    return decorator

if __name__ == "__main__":
    app = QApplication([])
    t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done"))
    t.start()
    app.exec_()

{'scan_qrcode': 'qrreader.scan_qrcode', 'scan_qr_from_image': 'qrreader.scan_qr_from_image'}.__init__


import numpy as np
import cv2

img = cv2.imread('road1.jpg',1)
cv2.imshow('Original',img)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray',gray)
edges = cv2.Canny(gray,250,500,apertureSize = 3)
# cv2.imshow('Edges',edges)
# lines = cv2.HoughLines(edges,1,np.pi/180,170)

# cv2.imshow('Lines',img)

k = cv2.waitKey(0)
if k == 27:         # wait for ESC key to exit
    cv2.destroyAllWindows()


import json
from trp import Document

def processDocument(doc):
    for i,page in enumerate(doc.pages,1):
        print("PAGE: {}\n====================".format(i))
        for line in page.lines:
            #print("Line: {}--{}".format(line.text, line.confidence))
            for word in line.words:
                #print("Word: {}--{}".format(word.text, word.confidence))
                None

def run():
    response = {}

    filePath = "aws1.json"
    with open(filePath, 'r') as document:
        response = json.loads(document.read())

    doc = Document(response)
    processDocument(doc)

run()


import os
import math
import random
import urllib.request
import asyncio
import textwrap

from PIL import Image, ImageDraw, ImageFont
from pyrogram.api.functions.messages import GetStickerSet
from pyrogram.api.types import InputStickerSetShortName
from pyrogram.errors.exceptions.bad_request_400 import YouBlockedUser

from userge import userge, Message, Config, pool

@userge.on_cmd("kang", about={
    'header': "kangs stickers or creates new ones",
    'usage': "Reply {tr}kang [emoji('s)] [pack number] to a sticker or "
             "an image to kang it to your userbot pack.",
    'examples': ["{tr}kang", "{tr}kang ðŸ¤”", "{tr}kang 2", "{tr}kang ðŸ¤” 2"]},
    allow_channels=False)
async def kang_(message: Message):
    """ kang a sticker """
    user = message.from_user
    if not user.username:
        user.username = user.first_name or user.id
    replied = message.reply_to_message
    photo = None
    emoji = None
    is_anim = False
    resize = False
    if replied and replied.media:
        if replied.photo:
            resize = True
        elif replied.document and "image" in replied.document.mime_type:
            resize = True
        elif replied.document and "tgsticker" in replied.document.mime_type:
            is_anim = True
        elif replied.sticker:
            emoji = replied.sticker.emoji
            is_anim = replied.sticker.is_animated
            if not replied.sticker.file_name.endswith('.tgs'):
                resize = True
        else:
            await message.edit("`Unsupported File!`")
            return
        await message.edit(f"`{random.choice(KANGING_STR)}`")
        photo = await userge.download_media(message=replied,
                                            file_name=Config.DOWN_PATH)
    else:
        await message.edit("`Nginx, can' proccess...`")
        return
    if photo:
        args = message.input_str.split()
        if not emoji:
            emoji = "ðŸ¤”"
        pack = 1
        if len(args) == 2:
            emoji, pack = args
        elif len(args) == 1:
            if args[0].isnumeric():
                pack = int(args[0])
            else:
                emoji = args[0]
        packname = f"a{user.id}_{user.username}_{pack}"
        packnick = f"{user.first_name} Pt.{pack}"
        cmd = '/newpack'
        if resize:
            photo = resize_photo(photo)
        if is_anim:
            packname = f"a{user.id}_by_{user.username}_anim{pack}"
            packnick = f"{user.first_name} Anim.{pack}"
            cmd = '/newanimated'

        @pool.run_in_thread
        def get_response():
            response = urllib.request.urlopen(
                urllib.request.Request(f'http://t.me/addstickers/{packname}'))
            return response.read().decode("utf8").split('\n')
        htmlstr = await get_response()
        if ("  A Telegram user has created "
                "the Sticker Set.") not in htmlstr:
            async with userge.conversation('Stickers') as conv:
                try:
                    await conv.send_message('/addsticker')
                except YouBlockedUser:
                    await message.edit('first **unblock** @Stickers')
                    return
                await conv.get_response(mark_read=True)
                await conv.send_message(packname)
                msg = await conv.get_response(mark_read=True)
                limit = "50" if is_anim else "120"
                while limit in msg.text:
                    pack += 1
                    packname = f"a{user.id}_{user.username}_{pack}"
                    packnick = f"{user.first_name} Pt.{pack}"
                    await message.edit("`Creating new branch: " + str(pack))
                    await conv.send_message(packname)
                    msg = await conv.get_response(mark_read=True)
                    if msg.text == "Invalid branch selected.":
                        await conv.send_message(cmd)
                        await conv.get_response(mark_read=True)
                        await conv.send_message(packnick)
                        await conv.get_response(mark_read=True)
                        await conv.send_document(photo)
                        await conv.get_response(mark_read=True)
                        await conv.send_message(emoji)
                        await conv.get_response(mark_read=True)
                        await conv.send_message("/publish")
                        if is_anim:
                            await conv.get_response(mark_read=True)
                            await conv.send_message(f"<{packnick}>")
                        await conv.get_response(mark_read=True)
                        await conv.send_message("/skip")
                        await conv.get_response(mark_read=True)
                        await conv.send_message(packname)
                        await conv.get_response(mark_read=True)
                        await message.edit(
                            f"`Build finished on new branch` !\n"
                            f"**Changelogs:** [here](t.me/addstickers/{packname})")
                        await asyncio.sleep(25)
                        await message.delete()
                        return
                await conv.send_document(photo)
                rsp = await conv.get_response(mark_read=True)
                if "Sorry, the file type is invalid." in rsp.text:
                    await message.edit("`Failed to add sticker, use` @Stickers "
                                       "`bot to add the sticker manually.`")
                    return
                await conv.send_message(emoji)
                await conv.get_response(mark_read=True)
                await conv.send_message('/done')
                await conv.get_response(mark_read=True)
        else:
            await message.edit("`Brewing a new Pack...`")
            async with userge.conversation('Stickers') as conv:
                try:
                    await conv.send_message(cmd)
                except YouBlockedUser:
                    await message.edit('first **unblock** @Stickers')
                    return
                await conv.get_response(mark_read=True)
                await conv.send_message(packnick)
                await conv.get_response(mark_read=True)
                await conv.send_document(photo)
                rsp = await conv.get_response(mark_read=True)
                if "Sorry, the file type is invalid." in rsp.text:
                    await args.edit("`Failed to add sticker, use` @Stickers "
                                    "`bot to add the sticker manually.`")
                    return
                await conv.send_message(emoji)
                await conv.get_response(mark_read=True)
                await conv.send_message("/publish")
                if is_anim:
                    await conv.get_response(mark_read=True)
                    await conv.send_message(f"<{packnick}>")
                await conv.get_response(mark_read=True)
                await conv.send_message("/skip")
                await conv.get_response(mark_read=True)
                await conv.send_message(packname)
                await conv.get_response(mark_read=True)
        await message.edit(f"`Build finished successfully!`\n"
                           f"**Changelogs:** [here](t.me/addstickers/{packname})")
        await asyncio.sleep(25)
        await message.delete()
        os.remove(photo)

@userge.on_cmd("stkrinfo", about={
    'header': "get sticker pack info",
    'usage': "reply {tr}stkrinfo to any sticker"})
async def sticker_pack_info_(message: Message):
    """ get sticker pack info """
    replied = message.reply_to_message
    if not replied:
        await message.edit("`I can't fetch info from nothing, can I ?!`")
        return
    if not replied.sticker:
        await message.edit("`Reply to a sticker to get the pack details`")
        return
    await message.edit("`Fetching details of the sticker pack, please wait..`")
    get_stickerset = await userge.send(
        GetStickerSet(
            stickerset=InputStickerSetShortName(
                short_name=replied.sticker.set_name)))
    pack_emojis = []
    for document_sticker in get_stickerset.packs:
        if document_sticker.emoticon not in pack_emojis:
            pack_emojis.append(document_sticker.emoticon)
    out_str = f"**Title:** `{get_stickerset.set.title}\n`" \
        f"**Short Name:** `{get_stickerset.set.short_name}`\n" \
        f"**Archived:** `{get_stickerset.set.archived}`\n" \
        f"**Official:** `{get_stickerset.set.official}`\n" \
        f"**Masks:** `{get_stickerset.set.masks}`\n" \
        f"**Animated:** `{get_stickerset.set.animated}`\n" \
        f"**Stickers:** `{get_stickerset.set.count}`\n" \
        f"**Emojis:**\n{' '.join(pack_emojis)}"
    await message.edit(out_str)

def resize_photo(photo: str) -> str:
    """ Resize the given photo to 512x512 """
    image = Image.open(photo)
    maxsize = (512, 512)
    if (image.width and image.height) < 512:
        size1 = image.width
        size2 = image.height
        if image.width > image.height:
            scale = 512 / size1
            size1new = 512
            size2new = size2 * scale
        else:
            scale = 512 / size2
            size1new = size1 * scale
            size2new = 512
        size1new = math.floor(size1new)
        size2new = math.floor(size2new)
        sizenew = (size1new, size2new)
        image = image.resize(sizenew)
    else:
        image.thumbnail(maxsize)
    os.remove(photo)
    photo = os.path.join(Config.DOWN_PATH, "sticker.png")
    if os.path.exists(photo):
        os.remove(photo)
    image.save(photo, "PNG")
    return photo

KANGING_STR = (
    "Build starting...",
    "Overclocking..",
    "Underclocking..",
    "Build started under commit: **Merge branch into new pack**",
    "Build started under commit: **Rewrite to improve performance**")

@userge.on_cmd("q", about={
    'header': "Quote a message",
    'usage': "{tr}q [text or reply to msg]"})
async def quotecmd(message: Message):
    """quotecmd"""
    asyncio.get_event_loop().create_task(message.delete())
    args = message.input_str
    replied = message.reply_to_message
    async with userge.conversation('QuotLyBot') as conv:
        try:
            if replied and not args:
                await conv.forward_message(replied)
            else:
                if not args:
                    await message.err('input not found!')
                    return
                await conv.send_message(args)
        except YouBlockedUser:
            await message.edit('first **unblock** @QuotLyBot')
            return
        quote = await conv.get_response(mark_read=True)
        if not quote.sticker:
            await message.err('something went wrong!')
        else:
            message_id = replied.message_id if replied else None
            await userge.send_sticker(chat_id=message.chat.id,
                                      sticker=quote.sticker.file_id,
                                      file_ref=quote.sticker.file_ref,
                                      reply_to_message_id=message_id)

@userge.on_cmd("plet", about={
    'header': "Get a Random RGB Sticker",
    'description': "Generates A RGB Sticker with provided text",
    'usage': "{tr}plet [text | reply]",
    'examples': "{tr}plet @theUserge"})
async def sticklet(message: Message):
    R = random.randint(0, 256)
    G = random.randint(0, 256)
    B = random.randint(0, 256)

    sticktext = message.input_or_reply_str
    if not sticktext:
        await message.edit("**Bruh** ~`I need some text to make sticklet`")
        return
    await message.delete()

    if message.reply_to_message:
        reply_to = message.reply_to_message.message_id
    else:
        reply_to = message.message_id

    # https://docs.python.org/3/library/textwrap.html#textwrap.wrap

    sticktext = textwrap.wrap(sticktext, width=10)
    sticktext = '\n'.join(sticktext)

    image = Image.new("RGBA", (512, 512), (255, 255, 255, 0))
    draw = ImageDraw.Draw(image)
    fontsize = 230

    font_file = await get_font_file()
    font = ImageFont.truetype(font_file, size=fontsize)

    while draw.multiline_textsize(sticktext, font=font) > (512, 512):
        fontsize -= 3
        font = ImageFont.truetype(font_file, size=fontsize)

    width, height = draw.multiline_textsize(sticktext, font=font)
    draw.multiline_text(
        ((512 - width) / 2, (512 - height) / 2), sticktext, font=font, fill=(R, G, B))

    image_name = "rgb_sticklet.webp"
    image.save(image_name, "WebP")

    await userge.send_sticker(
        chat_id=message.chat.id, sticker=image_name, reply_to_message_id=reply_to)

    # cleanup
    try:
        os.remove(font_file)
        os.remove(image_name)
    except Exception:
        pass

async def get_font_file():
    font_file_message_s = await userge.get_history("@FontsRes")
    font_file_message = random.choice(font_file_message_s)
    return await userge.download_media(font_file_message)


import pytest
import torch
from torch_geometric.nn import MedianAggregation, QuantileAggregation

from aggr_utils import aggr_harness

@pytest.mark.parametrize('q', [0., .1, .2, .3, .4, .5, .6, .7, .8, .9, 1.])
@pytest.mark.parametrize('interpolation', QuantileAggregation.interpolations)
def test_quantile_aggregation(dataloader, q, interpolation):
    torch.manual_seed(42)
    first_sample = next(iter(dataloader))
    in_channels = first_sample.num_node_features
    out_channels = in_channels * 2

    aggr = QuantileAggregation(q=q, interpolation=interpolation)
    post_proc = torch.nn.Linear(in_channels, out_channels)

    aggr_harness(aggr,
                 first_sample.num_nodes,
                 dataloader,
                 post_proc,
                 sorted_index=True)

def test_median_aggregation(dataloader):
    first_sample = next(iter(dataloader))
    in_channels = first_sample.num_node_features
    out_channels = in_channels * 2

    aggr = MedianAggregation()
    post_proc = torch.nn.Linear(in_channels, out_channels)

    aggr_harness(aggr, first_sample.num_nodes, dataloader, post_proc)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""Return a filter fasta file for list of accession"""

import argparse
import sys

from Bio import SeqIO

desc = "Filter a fasta file based on ."
command = argparse.ArgumentParser(
    prog="fasta_filter_to_accession",
    description=desc,
    usage="%(prog)s [options] fasta accessions",
)
command.add_argument(
    "-o",
    "--output",
    default=sys.stdout,
    type=argparse.FileType("w"),
    nargs="?",
    help="Output result to, default:stdout",
)
command.add_argument("fasta", type=argparse.FileType("r"), help="Fasta file to filter")
command.add_argument(
    "access",
    metavar="accessions",
    type=str,
    nargs="+",
    help="List of accession to extract",
)

if __name__ == "__main__":
    """Performed job on execution script"""
    args = command.parse_args()
    output = args.output
    access = set(args.access)
    for seq in SeqIO.parse(args.fasta, "fasta"):
        if seq.id in access:
            SeqIO.write(seq, output, "fasta")
            access.remove(seq.id)
    if len(access) > 0:
        sys.stderr.write("Accessions not found : " + str(" - ").join(access) + "\n")


#!/usr/bin/env python
# vim: tabstop=4:softtabstop=4:shiftwidth=4:expandtab:

from __future__ import print_function

import os
import re
import sqlite3
import argparse
from ephem import readtle
from math import pi, degrees
from requests import get as wget
from tempfile import mkstemp
from zipfile import ZipFile

# From https://physics.stackexchange.com/questions/113300/why-does-earth-have-a-minimum-orbital-period
# Assuming for a sec that there was no atmosphere, and the earth is a perfectly
# smooth sphere with a radius of 6378km and we're orbiting just above the surface,
# we can calculate that the minimum orbital period is about 84.47 minutes
minimum_period = 84.47

# we seem to think of 60 miles / 100km as "space" ... that's the altitude where you
# get your astronaut wings. The thermosphere is from 80-500km altitude, and below
# about 160km a satellite in a *circular* orbit can interact with enough of this
# thin atmosphere to rapidly decay. Highly elliptical orbits are quite different.
#
# Some photoreconnaissance satellites had a perigee as low as 152km, Molniya
# orbits can come closer - about 120km
minimum_orbit = 100

# Perhaps I should use the python-sgp4 checksum fixer...
tle_line_length = 69

def readzip(zip_archive, file_name):
    '''read a file from a zip archive'''
    buf = ''
    with ZipFile(zip_archive) as zf:
        with zf.open(file_name) as zfd:
            buf = zfd.read()
    return buf

def readfile(fn, mode='rb'):
    '''read the contents of a file'''
    with open(fn, mode) as fd:
        return fd.read()

def fetch(args, url, headers=None):
    '''download a file'''
    fname = os.path.basename(url)
    if not os.path.exists(fname) or args.refetch is True:
        if args.do_print:
            print('download {}'.format(url))
        if headers is None:
            headers = {}
        with wget(url, timeout=5, stream=True, headers=headers) as resp:
            if resp.ok:
                (tempfd, tempfn) = mkstemp(dir='.', prefix='tmp')
                with open(tempfn, 'wb') as fd:
                    for chunk in resp.iter_content(chunk_size=32768):
                        if chunk:
                            fd.write(chunk)
                os.rename(tempfn, fname)

            else:
                print('URL {} failed: {}\n{}'.format(url, resp.status_code, resp.content))

    if args.do_print:
        print('file {}, size {}'.format(fname, os.path.getsize(fname)))

def check_violated_constraints(r):
    bad_checks = []
    if r['norad_catalog'] <= 0:
        bad_checks.append("norad_catalog")
    if not (6<= len(r['intldes']) <=8):
        bad_checks.append("intldes")
    if not (-180 <= r['inclination'] <=180):
        bad_checks.append("inclination")
    if r['classified'] not in [0, 1]:
        bad_checks.append('classified')
    if r['apogee'] < minimum_orbit:
        bad_checks.append('apogee')
    if r['perigee'] < minimum_orbit:
        bad_checks.append('perigee')
    if r['period'] < minimum_period:
        bad_checks.append('period')
    if r['mean_motion'] <= 0.0:
        bad_checks.append('mean_motion')
    if r['eccentricity'] < 0.0:
        bad_checks.append('eccentricity')
    if r['semimajor_axis'] < 0.0:
        bad_checks.append('semimajor_axis')
    if len(r['line1']) != tle_line_length:
        bad_checks.append('line1')
    if len(r['line2']) != tle_line_length:
        bad_checks.append('line2')
    if bad_checks == []:
        bad_checks.append('UNIQUE')
    return bad_checks

def dbinit(args):
    '''initialize a database connection'''
    create_table_sql = '''
      CREATE TABLE "tles" (
        "norad_catalog"   INTEGER NOT NULL CHECK(norad_catalog>0) UNIQUE,
        "classified"      INTEGER NOT NULL DEFAULT 0 CHECK(classified==0 or classified==1),
        "payload"         INTEGER NOT NULL DEFAULT 0 CHECK(payload==0 or payload==1),
        "inclination"     REAL NOT NULL CHECK(inclination>=-180 and inclination <=180),
        "period"          REAL NOT NULL CHECK(period>={0}),
        "apogee"          REAL NOT NULL CHECK(apogee>={1} or perigee>={1}),
        "perigee"         REAL NOT NULL CHECK(apogee>={1} or perigee>={1}),
        "mean_motion"     REAL NOT NULL CHECK(mean_motion>0),
        "eccentricity"    REAL NOT NULL CHECK(eccentricity>=0),
        "semimajor_axis"  REAL NOT NULL CHECK(semimajor_axis>=0),
        "epoch"           TIMESTAMP NOT NULL,
        "intldes"         VARCHAR(8)  NOT NULL CHECK(length(intldes)>=6 and length(intldes)<=8) UNIQUE,
        "name"            VARCHAR(80) NOT NULL DEFAULT "",
        "line1"           TEXT NOT NULL CHECK(length(line1)=={2}),
        "line2"           TEXT NOT NULL CHECK(length(line2)=={2}),
        PRIMARY KEY("norad_catalog")
    ) WITHOUT ROWID;

      CREATE INDEX ix_name ON tles (name);
      CREATE INDEX ix_intldes ON tles (intldes);
      CREATE INDEX ix_classified ON tles (classified);
      CREATE INDEX ix_payload ON tles (payload);
      CREATE INDEX ix_eccentricity ON tles (eccentricity);
      CREATE INDEX ix_perigee ON tles (perigee);
      CREATE INDEX ix_apogee ON tles (apogee);
      CREATE INDEX ix_period ON tles (period);
      CREATE INDEX ix_mean_motion ON tles (mean_motion);
      CREATE INDEX ix_inclination ON tles (inclination);
      CREATE INDEX ix_semimajor_axis ON tles (semimajor_axis);
      CREATE INDEX ix_epoch ON tles (epoch);
    '''.format(minimum_period, minimum_orbit, tle_line_length)

    dbh = sqlite3.connect(args.database)
    dbh.row_factory = sqlite3.Row
    if args.initdb:
        dbh.execute('drop table if exists tles')

    try:
        dbh.executescript(create_table_sql)
    except sqlite3.OperationalError:
        if args.do_print:
            print("database exists; not reinitializing")
        pass
    return dbh

def orbital_properties(n, e):
    '''Compute some nice orbital metadata

    Slightly modified from space-track.org FAQ:

    We added semi-major axis, period, apogee, and perigee to the TLE and TLE_latest
    API classes so that users can filter their queries by these values, download
    only the data they need, and decrease the amount of the site's bandwidth that
    they use. Now, all the orbital elements in the satellite catalog (SATCAT) are
    available in the TLE class. However, the value of the same element (e.g.
    apogee) may not match exactly.

    Every TLE already displays a value for the object's mean motion ("n") and
    eccentricity ("e"), so we derive these additional four values using the
    following calculations:

    period = 1440/n
    Using mu, the standard gravitational parameter for the earth (398600.4418),
    semi-major axis "a" = (mu/(n*2*pi/(24*3600))^2)^(1/3)
    Using semi-major axis "a", eccentricity "e", and the Earth's radius in km,
    apogee = (a * (1 + e))- 6378.135
    perigee = (a * (1 - e))- 6378.135
    '''

    earth_radius = 6378.135
    mu = 398600.4418
    seconds_per_day = 86400.0
    minutes_per_day = seconds_per_day / 60.0

    semimajor_axis = (mu/(n * 2.0 * pi / seconds_per_day) ** 2 ) ** (1.0/3.0)
    apogee = (semimajor_axis * (1 + e)) - earth_radius
    perigee = (semimajor_axis * (1 - e)) - earth_radius
    period = minutes_per_day / n

    return (semimajor_axis, apogee, perigee, period)

def build_record(tle, classified):
    '''build a record to insert into the database'''
    es = readtle(*tle)
    semimajor_axis, apogee, perigee, period = orbital_properties(es._n, es._e)
    intldes = tle[1][9:17].strip()
    payload = True
    if re.search(r'\b(DEB(RIS)?|R/B(\(\d+\))?|r)\b', es.name):
        payload = False
    return {
        'norad_catalog': es.catalog_number,
        'intldes': intldes,
        'name': es.name,
        'classified': classified,
        'payload': payload,
        'inclination': degrees(es._inc),
        'mean_motion': es._n,
        'period': period,
        'eccentricity': es._e,
        'semimajor_axis': semimajor_axis,
        'epoch': es._epoch.datetime(),
        'apogee': apogee,
        'perigee': perigee,
        'line1': tle[1].strip(),
        'line2': tle[2].strip()
    }

def dbinsert(dbh, tle, classified=False, update=False, do_print=False):
    '''insert a record into the database'''
    rec = build_record(tle, classified)
    rv = True
    columns = ', '.join(rec.keys())
    placeholders = ':'+', :'.join(rec.keys())
    u = ""
    if update:
        u = "OR REPLACE"
    sql = 'INSERT %s INTO tles (%s) VALUES (%s)' % (u, columns, placeholders)
    try:
        dbh.execute(sql, rec)
    except sqlite3.IntegrityError:
        if do_print:
            print("error inserting {}/{}/{}".format(rec['name'], rec['norad_catalog'], rec['intldes']))
            print("constraints: ", check_violated_constraints(rec))
            print(rec)
        rv = False
    return (rv, rec['name'], rec['norad_catalog'], rec['intldes'])

def do_download(args):
    '''download the datafiles'''
    fake_hdr = {
        'Referer': 'https://tle.info/joomla/index.php',
        'User-Agent': 'Wget/1.71.1 (linux-gnu)',
        'Accept-Encoding': 'identity',
    }

    fetch(args, 'https://www.prismnet.com/~mmccants/tles/classfd.zip')
    fetch(args, 'https://www.tle.info/data/ALL_TLE.ZIP', fake_hdr)

def load_compressed_tle(args, archive, member, dbh, classified=0):
    '''extract the named member from a zip archive, parse out the TLE entries, and load them into the database'''
    buf = readzip(archive, member)
    tles = {}
    for sat in re.findall('(?P<name>^.+?\n)?(?P<line1>^1 .+?)\n(?P<line2>^2 .+?)\n', buf, re.MULTILINE):
        tles[sat[0].strip()] = (sat[0].strip(), sat[1], sat[2])

    if args.do_print:
        print("file {} contains {} TLEs".format(member, len(tles)))

    for sat in tles:
        dbinsert(dbh, tles[sat], classified, update=args.update, do_print=args.do_print)

    dbh.commit()

def main():
    ap = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    ap.add_argument('-d', '--database', dest='database', default='tles.sqlite', help="database file")
    ap.add_argument('-i', '--initdb', dest='initdb', default=False, action='store_true', help="initialize the database")
    ap.add_argument('-r', '--refetch', dest='refetch', default=False, action='store_true', help="download new datafiles")
    ap.add_argument('-u', '--update', dest='update', default=False, action='store_true', help="update database records")
    ap.add_argument('-q', '--quiet', dest='do_print', default=True, action='store_false')
    args = ap.parse_args()

    dbh = dbinit(args)

    do_download(args)

    load_compressed_tle(args, 'ALL_TLE.ZIP', 'ALL_TLE.TXT', dbh)
    load_compressed_tle(args, 'classfd.zip', 'classfd.tle', dbh, classified=1)

    r = dbh.execute('select count(*) from tles')
    n = r.fetchone()['count(*)']
    print("database contains ", n, " records")

if __name__ == '__main__':
    main()


from kafka import KafkaConsumer

class KafkaConsumers(object):

    def __init__(self, kafka_host, kafka_port, kafka_topic, group_id):
        self._host = kafka_host
        self._port = kafka_port
        self._topic = kafka_topic
        self._group_id = group_id
        self._consumer = None
        try:
            self._consumer = KafkaConsumer(bootstrap_servers="%s:%s" % (self._host, self._port),
                                           group_id=self._group_id)
            self._consumer.subscribe(self._topic)
        except Exception as e:
            print(e)

    def consumer_pull_data(self):
        """

        :return:
        """
        try:
            msg = self._consumer.poll(timeout_ms=2000, max_records=1)
            message = list(msg.values())
            if len(message) > 0:
                message = message[0][0]
                return message
        except Exception as e:
            print(e)

def main():
    consumer = KafkaConsumers(kafka_host="10.242.111.211", kafka_port=9092, kafka_topic="test", group_id="mytest")
    while True:
        consumer.consumer_pull_data()

if __name__ == '__main__':
    main()


from datetime import datetime
import pytest
import pandas as pd
from pandas.testing import assert_frame_equal
from covsirphy import Validator, ODEModel, SIRModel, SIRFModel
from covsirphy import NAFoundError, NotIncludedError, NotSubclassError, UnExpectedTypeError, EmptyError
from covsirphy import UnExpectedValueRangeError, UnExpectedValueError, UnExpectedLengthError, UnExpectedNoneError

def test_none():
    Validator(1, "target", accept_none=True)
    with pytest.raises(UnExpectedNoneError):
        Validator(None, "target", accept_none=False)

def test_subclass():
    v = Validator(SIRFModel, name="model")
    with pytest.raises(NotSubclassError):
        v.subclass(SIRModel)
    assert v.subclass(ODEModel) == SIRFModel

def test_instance():
    v = Validator("covsirphy")
    with pytest.raises(UnExpectedTypeError):
        v.instance(int)
    assert v.instance(str) == "covsirphy"

def test_dataframe():
    with pytest.raises(UnExpectedTypeError):
        Validator("string").dataframe()
    with pytest.raises(EmptyError):
        Validator(pd.DataFrame()).dataframe(empty_ok=False)
    df = pd.DataFrame({"A": [1, 2], "B": [3, 4], "C": pd.date_range("01Jan2022", "02Jan2022", freq="D")})
    v = Validator(df, "dataframe")
    with pytest.raises(UnExpectedTypeError):
        v.dataframe(time_index=True)
    with pytest.raises(NotIncludedError):
        v.dataframe(columns=["D"])
    assert_frame_equal(v.dataframe(), df)
    assert_frame_equal(v.dataframe(columns=["A"]), df)

def test_float():
    assert Validator(None).float(default=1.2) == 1.2
    with pytest.raises(UnExpectedTypeError):
        Validator("string").float()
    with pytest.raises(UnExpectedValueRangeError):
        Validator(1.2).float(value_range=(0, 1.1))
    with pytest.raises(UnExpectedValueRangeError):
        Validator(1.2).float(value_range=(1.5, 2))
    assert Validator(1.2).float() == 1.2
    assert Validator(1.6564).float(digits=3) == 1.66
    assert Validator(0).float(digits=3) == 0

def test_int():
    assert Validator(None).int(default=2) == 2
    with pytest.raises(UnExpectedTypeError):
        Validator("string").int()
    with pytest.raises(UnExpectedTypeError):
        Validator(1.2).int()
    with pytest.raises(UnExpectedValueRangeError):
        Validator(2).int(value_range=(0, 1))
    with pytest.raises(UnExpectedValueRangeError):
        Validator(2).int(value_range=(3, 4))
    assert Validator(2.5).int(round_ok=True) == 2

def test_tau():
    assert Validator(None).tau(default=720) == 720
    with pytest.raises(UnExpectedValueError):
        Validator(11).tau()
    assert Validator(360).tau() == 360

def test_date():
    assert Validator(None).date(default=pd.Timestamp("01Jan2022")) == pd.Timestamp("01Jan2022")
    assert Validator(pd.Timestamp("01Jan2022")).date() == pd.Timestamp("01Jan2022")
    assert Validator(datetime(year=2022, month=1, day=1)).date() == pd.Timestamp("01Jan2022")
    assert Validator("01Jan2022").date() == pd.Timestamp("01Jan2022")
    with pytest.raises(UnExpectedTypeError):
        Validator("hello").date()
    v = Validator("2022-01-01")
    with pytest.raises(UnExpectedValueRangeError):
        v.date(value_range=(None, pd.Timestamp("2021-12-31")))
    with pytest.raises(UnExpectedValueRangeError):
        v.date(value_range=(pd.Timestamp("2022-02-01"), None))

def test_sequence():
    assert Validator(None).sequence(default=[1, 2]) == [1, 2]
    with pytest.raises(UnExpectedTypeError):
        Validator(1).sequence()
    with pytest.raises(UnExpectedTypeError):
        Validator([[1, 2], 2]).sequence(flatten=True)
    assert Validator([[1, 2], [3, 4]]).sequence(flatten=True) == [1, 2, 3, 4]
    assert Validator([1, 2, 4, 2, 2]).sequence(unique=True) == [1, 2, 4]
    v = Validator([1, 2, 3])
    with pytest.raises(UnExpectedValueError):
        v.sequence(candidates=[1, 4, 5])
    assert v.sequence(candidates=[1, 2, 3, 4]) == [1, 2, 3]
    with pytest.raises(UnExpectedLengthError):
        v.sequence(length=2)
    assert v.sequence(length=3) == [1, 2, 3]

def test_dict():
    assert Validator(None).dict(default={1: 2}) == {1: 2}
    with pytest.raises(UnExpectedTypeError):
        Validator(1).dict()
    with pytest.raises(NAFoundError):
        Validator({1: 2}).dict(required_keys=[3, 4], errors="raise")
    assert Validator({1: 2}).dict(default={3: 5, 6: 8}, required_keys=[3, 4]) == {1: 2, 3: 5, 6: 8, 4: None}

def test_kwargs():
    kwargs = {"target": SIRModel, "unnecessary_key": True}
    v = Validator(kwargs)
    result_dict = v.kwargs(functions=Validator, default={"name": "target name"})
    assert isinstance(Validator(**result_dict), Validator)


import cv2
import numpy as np
import imutils
import pytesseract
from PIL import Image
import re

pytesseract.pytesseract.tesseract_cmd = "C:/Program Files (x86)/Tesseract-OCR/tesseract.exe"
tessdata_dir_config = '--tessdata-dir "C:/Program Files (x86)/Tesseract-OCR/tessdata"'

#pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe"
img = cv2.imread('d.jpg')

img = imutils.resize(img, width= 500) #standart olarak genişliği 500 ayarladım

cv2.imshow("original", img)
cv2.waitKey(0)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.waitKey(0)

gray = cv2.bilateralFilter(gray, 11, 17, 17)#Blur to reduce noise
cv2.waitKey(0)

edged = cv2.Canny(gray, 170, 200)
cv2.imshow("edged",edged)
cv2.waitKey(0)

cnts, new = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

#RETR LİST = tüm konturları gözden geçirir ancak herhangi bir bağlılık oluşturmaz oluşturmaz
#chain_approx_simple = tüm gereksiz noktaları kaldırır ve hafızadan tasarruf ederek konturu sıkıştırır

image1 = img.copy()
cv2.drawContours(image1, cnts, -1, (0,255,255), 3)
cv2.imshow("Canny after contouring", image1)
cv2.waitKey(0)

cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:30]
NumberPlateCount = None

image2 = img.copy()
cv2.drawContours(image2 , cnts, -1, (0,255,0),3)
cv2.waitKey(0)

count = 0  #içerisinde plaka olma ihtimali en yüksek olan konturleri bulmak için for döngüsü oluşturduk
name = 1

for i in cnts:
    perimeter = cv2.arcLength(i, True) #konturun cevresini hesapladık, true değeri ile de kapalı şekil oldugunu belirttik
    approx = cv2.approxPolyDP(i, 0.02*perimeter, True) #cevrenin bir şekle yaklaşması için hassasiyet faktörü olan 0.02 ile carptık

    if(len(approx) == 4):    #if our approximated contour has four points, then we can assume that we have found our screen
        NumberPlateCount = approx

        x, y, w, h =cv2.boundingRect(i)                     #############################
        crp_img = img[y+3:y+h+3, x-1:x+w+1]                 #                           #
        cv2.imwrite(str(name)+ '.jpg', crp_img)             #                           # (y+h)
        name+=1                                             #                           #
        break                                               #############################
                                                          #(x,y)        (x+w)

cv2.drawContours(img, [NumberPlateCount], -1, (0,255,0), 3) #arac plakası olarak tanımladıgımız ana resim üzerine kontur uyguluyoruz
cv2.imshow("Final Image", img)
cv2.waitKey(0)

crop_img_loc = '1.jpg' #sadece plakayı alıyoruz
cv2.imshow("Cropped Image", cv2.imread(crop_img_loc))

text = pytesseract.image_to_string(crop_img_loc,config=tessdata_dir_config)
clean_text = re.sub('[\W_]+', ' ', text)
print("Arac Plakası: ", clean_text)

cv2.waitKey(0)
cv2.destroyAllWindows()


#!/usr/bin/python3
# 3.7.1
# NOTE: Excel is parsed beyond our control in 3rd party app, so we must modify the XLSX files accordingly before processing...

import re
import math
import os
from openpyxl import load_workbook
from openpyxl.utils import column_index_from_string

# Takes a string input , isolates digits, remove leading zeros.
def round_string(string: str):
    numbers = re.findall(r"[0-9.]+", string)
    for i, number in enumerate(numbers):
        string = string.replace(numbers[i], str(math.ceil(float(number))))
    return string

def capy_sanitize(sheet, row, column):

        first_string = ""
        second_string = ""

        for i in range(7, 13):
            first_string += " " + str(sheet.cell(row=row, column=i).value)
        for i in range(7, 13):
            second_string += " " + str(sheet.cell(row=row+1, column=i).value)

        first_string = first_string.replace("None", "")
        second_string = second_string.replace("None", "")

        # Round all floats , then make sure , is placed in larger #s.

        floats = re.findall(r"([0-9]*\.[0-9]*)", first_string)
        for float_string in floats:
            first_string = first_string.replace(
                float_string,
                  "{:,}".format(int(round(float(float_string)))))

        floats = re.findall(r"([0-9]*\.[0-9]*)", second_string)
        for float_string in floats:
            second_string = second_string.replace(
                float_string,
                 "{:,}".format(int(round(float(float_string)))))

        numbers = re.findall(r"([0-9]{4,})", second_string)
        for number in numbers:
             second_string = second_string.replace(str(number), "{:,}".format(int(number)))

        # Blank out 2 rows from cols 7-13

        sheet.cell(row=row, column=7).value = ""
        sheet.cell(row=row, column=8).value = ""
        sheet.cell(row=row, column=9).value = ""
        sheet.cell(row=row, column=10).value = ""
        sheet.cell(row=row, column=11).value = ""
        sheet.cell(row=row, column=12).value = ""
        sheet.cell(row=row, column=13).value = ""

        sheet.cell(row=row+1, column=7).value = ""
        sheet.cell(row=row+1, column=8).value = ""
        sheet.cell(row=row+1, column=9).value = ""
        sheet.cell(row=row+1, column=10).value = ""
        sheet.cell(row=row+1, column=11).value = ""
        sheet.cell(row=row+1, column=12).value = ""
        sheet.cell(row=row+1, column=13).value = ""

        sheet.cell(row=row, column=7).value = first_string
        sheet.cell(row=row+1, column=7).value = second_string

# Once we have identified "Additional Stencils" header:
def addl_stencils_sanitize(sheet, row, column):
    # Check for "X" in the "need" column.
    if sheet.cell(row=row+3, column=column+1).value == "X":
        # Loop through remaining rows in sheet
        for i in range(row, sheet.max_row+1):
            if sheet.cell(row=i, column=column+6).value is not None and str(sheet.cell(row=i, column=column+6).value).strip() is not "":
                val = str(sheet.cell(row=i, column=column+6).value).strip()
                if "Description" not in val and val is not "" and not val.startswith("QQQQQ"):
                    sheet.cell(row=i, column=column+6).value = "QQQQQ" + val

def process_file(filename=None):

    wb = load_workbook(filename, data_only=True)
    # Iterate over all sheets.
    for sheet in wb:

        if wb.worksheets[0].cell(row=100, column=100).value != "processed":

        # format the Color value as Text
            if wb.worksheets[0].cell(row=7, column=3).value is not None:
                if "WHITE" in wb.worksheets[0].cell(row=7, column=3).value or "BLACK" in wb.worksheets[0].cell(row=7, column=3).value:
                    wb.worksheets[0].cell(row=7, column=3).value = wb.worksheets[0].cell(row=7, column=3).value
                else:
                    wb.worksheets[0].cell(row=7, column=3).value = "YELLOW"
            else:
                wb.worksheets[0].cell(row=7, column=3).value = "YELLOW"

            # Iterate all rows
            for row in sheet.iter_rows():
                # iterate each cell
                for cell in row:
                    if cell.value == "CAPY":
                        capy_sanitize(sheet, cell.row, column_index_from_string(cell.column))
                    elif cell.value == "Additional Stencils":
                        addl_stencils_sanitize(sheet, cell.row, column_index_from_string(cell.column))

   # find the last row & last column , leave a "processed" tag.
    wb.worksheets[0].cell(row=100, column=100).value = "processed"

    wb.save(filename)

# Generate list of files in current directory.
path = os.path.normpath(r"C:\Input")
os.chdir(path)
files = os.listdir(path)

# Loop through & process files.
for file in files:
    if file.endswith('xlsx'):
        process_file(file)


# Starts up WinAppDriver before the test
import subprocess
import os
import psutil

class Driver:
    def __init__(self, driver_path):
        self.f = open(os.devnull, 'w')
        self.process = None
        self.driver_path = driver_path

    def set_up_driver(self, path=None):
        """Starts the Windows Application Driver as a subprocess.

        Arguments detailed:
        | =Argument= | =Input=                                |
        | path       | Location of Windows Application Driver |

        | =Return=   | =Output=                               |
        | None       | None                                   |
        """
        if path is None:
            path = self.driver_path
        si = subprocess.STARTUPINFO()
        si.dwFlags = subprocess.STARTF_USESHOWWINDOW
        si.wShowWindow = 0
        self.process = subprocess.Popen([path], startupinfo=si, creationflags=subprocess.CREATE_NEW_CONSOLE)

    def tear_down_driver(self):
        """Stops the Windows Application Driver."""
        process = psutil.Process(self.process.pid)
        for pro in process.children(recursive=True):
            pro.kill()
        process.kill()


from django.contrib import admin
from .models import Add_College_and_University, member
from import_export.admin import ImportExportModelAdmin
from Notification.models import *

class NotificationInlineAdmin(admin.TabularInline):
  model = Notification

class MemberAdmin(admin.ModelAdmin):
  fieldsets = (
      ('Personal Info', {
          'fields': ('user', 'profile', 'first_name', 'middle_name', 'last_name', 'email', 'gender', 'primary_phone', 'secondry_phone', 'birth_date', 'Terms_and_Conditions',)
      }),

      ('Package Info', {
          'fields': ('Paid', 'amount', 'payment_id', 'pin_code', 'package', 'document')
      }),

      ('other Info', {
          'fields': ('address_1', 'address_2', 'city', 'state', 'Country',)
      }),
  )
  search_fields = ['pin_code']

admin.site.register(member, MemberAdmin)

class Add_College_and_UniversityAdmin(admin.ModelAdmin):
  fieldsets = (
      ('College or University Info', {
          'fields': ('user', 'Profile_Picture', 'College_or_University_Name', 'Course', 'Stream', 'City', 'Pin_Code', 'slug')
      }),
   )
admin.site.register(Add_College_and_University, Add_College_and_UniversityAdmin)


import sys, time, requests, json
sys.path.append(".")
import tushare as ts
from getshare import getshare
import pandas as pd
class share:

    def __init__(self,code):
        self.code = code
        self.have = 0 # 持仓数量
        self.yesterdayMax = None
        self.oneHand = self.iniOneHand()
        self.decimal = self.iniDecimal()
        self.state = False # 状态 是否金叉

    def iniOneHand(self):
        return 10 if self.code[:2] in ['10', '11', '12'] else 100

    def iniDecimal(self):
        return 3 if self.code[:2] in ['16', '15', '51'] else 2 #etf三位

    def get_now_price(self):
        return float(ts.get_realtime_quotes(
            self.code
        )['price'][0])

    def isMa(self,a,b):
        pass

    def addTime(self,start, long):
        s = (start%100 + long)%60
        m = (start%10000/100 + int((start%100 + long)/60))%60
        h = int(start/10000 + int((start%10000/100 + int((start%100 + long)/60))/60))
        return h*10000 + m*100 + s

    def get_data(self,code = '300033',type = 't', date=None, long = '60',retry_count=3, pause=0.001):
        """
            获取交易数据
        Parameters
        ------
            code: string
                    股票代码 e.g. 600848
            type: string, 默认 t
                    数据单位
                    t   : 每笔交易明细
                    t15 : 每15秒一周期
                    t30 : 每30秒一周期
                    m   : 每一分钟一周期
                    m5  : 每5分钟一周期
                    d   : 每一天一周期
                    d5  : 每五天一周期
            date: string
                    为空时，为当日数据
                    开始日期 format：YYYY-MM-DD
            long: int, 默认 60
                    数据个数
            retry_count : int, 默认 3
                    如遇网络等问题重复执行的次数
            pause : int, 默认 0
                    重复请求数据过程中暂停的秒数，防止请求间隔时间太短出现的问题
        return
        -------
            DataFrame 股票交易数据(DataFrame)
        """

        for _ in range(retry_count):
            time.sleep(pause)
            try:
                if date is None:
                    url = 'http://push2ex.eastmoney.com/getStockFenShi?pagesize=6644&ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wzfscj&pageindex=0&;id=%s&sort=1&ft=1&code=%s&market=%s&_=%s'
                    maker = 1 if code[:1] in ['5', '6', '9'] or code[:2] in ['11', '13'] else 0
                    re = requests.get(url%(self.code, self.code, maker, int(time.time())))
                    lines = json.loads(re.text)['data']['data']
                    df = pd.DataFrame(lines)
                    df['p'] = df['p'].map(lambda x: x/1000)
                    if type[0] == 't'and len(type) ==1 :
                        bs_type = {'1':u'买入',
                                    '2': u'卖出',
                                    '4': u'-'}
                        df['bs'] = df['bs'].map(lambda x: bs_type[str(x)])

                    elif type[0] == 't':
                        n = int(type[1:])
                        count = self.addTime(91500,n)
                        liopen = []
                        lilow = []
                        lihigh = []
                        liclose = []
                        litime = []

                        liopen.append(df['p'][0])
                        low = df['p'][0]
                        high = df['p'][0]
                        close = df['p'][0]
                        ntime = df['t'][0]

                        for tup in zip(df['t'], df['p']):

                            if tup[0] <= count:
                                if tup[1] > high:
                                    high = tup[1]
                                elif tup[1] < low:
                                    low = tup[1]
                                close = tup[1]
                                ntime = tup[0]

                            else:
                                lilow.append(low)
                                lihigh.append(high)
                                liclose.append(close)
                                litime.append(ntime)

                                liopen.append(tup[1])
                                low = tup[1]
                                high = tup[1]
                                close = tup[1]
                                ntime = tup[0]
                                count = self.addTime(count,n)

                        lilow.append(low)
                        lihigh.append(high)
                        liclose.append(close)
                        litime.append(ntime)
                        df = pd.DataFrame({'time':litime,'open':liopen,'close': liclose, 'low':lilow,'high':lihigh})

            except Exception as e:
                print(e)
            else:
                return df

        raise IOError('获取失败，请检查网络.')

    def kdj(self,a):
        df = self.get_data("113038",'t60')
        low_list = df['low'].rolling(9, min_periods=9).min()
        low_list.fillna(value=df['low'].expanding().min(), inplace=True)
        high_list = df['high'].rolling(9 , min_periods=9 ).max()
        high_list.fillna(value=df['high'].expanding().max(), inplace=True)
        rsv = (df['close'] - low_list) / (high_list - low_list) * 100
        df['k'] = pd.DataFrame(rsv).ewm(com=2).mean()
        df['d'] = df['k'].ewm(com=2).mean()
        df['j'] = 3 * df['k'] - 2 * df['d']
        df['kdj'] = df['k']>df['d']
        a[2] = df.iloc[-1]['close']
        a[3] = self.state
        self.state = df.iloc[-1]['kdj']
        return self.state

if __name__=='__main__':
    # s = share(code='300033')
    # print(s.get_data(type='t60'))
    import matplotlib.pyplot as plt
    plt.close("all")
    import numpy as np
    ts = pd.Series(np.random.randn(1000), index=pd.date_range("1/1/2000", periods=1000))
    ts = ts.cumsum()
    ts.plot()


import pathlib
import shutil
import tempfile

import snip_copy
from snip_copy.__main__ import main

def test_main():
    ex_hw_folder = pathlib.Path(snip_copy.__file__).parents[1] / 'test' / 'ex_hw'
    file_list = [ex_hw_folder / 'rubric.py',
                 ex_hw_folder / 'rubric.ipynb']

    for file in file_list:
        # build temp directory, load in an example file
        tmp_dir = pathlib.Path(tempfile.TemporaryDirectory().name)
        tmp_dir.mkdir()
        file_copy = tmp_dir / file.name
        shutil.copyfile(file, file_copy)

        # call "CLI"
        args = [str(file_copy), '--cmd', '# ?!', '-s', 'c']
        main(args=args)

        # check that new files are created via CLI
        assert file_copy.with_stem('rubricsolution').exists()
        assert file_copy.with_stem('rubricstudent').exists()

        # cleanup
        shutil.rmtree(tmp_dir)