<commit_before># -*- coding: utf-8 -*-
import traceback
from enum import IntEnum
from typing import Sequence, Optional, Dict

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QMenu, QHBoxLayout, QLabel, QVBoxLayout, QGridLayout, QLineEdit,
                             QPushButton, QAbstractItemView, QComboBox, QCheckBox)
from PyQt5.QtGui import QFont, QStandardItem, QBrush

from electrum.util import bh2u, NotEnoughFunds, NoDynamicFeeEstimates
from electrum.i18n import _
from electrum.lnchannel import AbstractChannel, PeerState, ChannelBackup, Channel
from electrum.wallet import Abstract_Wallet
from electrum.lnutil import LOCAL, REMOTE, format_short_channel_id, LN_MAX_FUNDING_SAT
from electrum.lnworker import LNWallet

from .util import (MyTreeView, WindowModalDialog, Buttons, OkButton, CancelButton,
                   EnterButton, WaitingDialog, MONOSPACE_FONT, ColorScheme)
from .amountedit import BTCAmountEdit, FreezableLineEdit


ROLE_CHANNEL_ID = Qt.UserRole


class ChannelsList(MyTreeView):
    update_rows = QtCore.pyqtSignal(Abstract_Wallet)
    update_single_row = QtCore.pyqtSignal(Abstract_Wallet, AbstractChannel)
    gossip_db_loaded = QtCore.pyqtSignal()

    class Columns(IntEnum):
        SHORT_CHANID = 0
        NODE_ALIAS = 1
        CAPACITY = 2
        LOCAL_BALANCE = 3
        REMOTE_BALANCE = 4
        CHANNEL_STATUS = 5

    headers = {
        Columns.SHORT_CHANID: _('Short Channel ID'),
        Columns.NODE_ALIAS: _('Node alias'),
        Columns.CAPACITY: _('Capacity'),
        Columns.LOCAL_BALANCE: _('Can send'),
        Columns.REMOTE_BALANCE: _('Can receive'),
        Columns.CHANNEL_STATUS: _('Status'),
    }

    filter_columns = [
        Columns.SHORT_CHANID,
        Columns.NODE_ALIAS,
        Columns.CHANNEL_STATUS,
    ]

    _default_item_bg_brush = None  # type: Optional[QBrush]

    def __init__(self, parent):
        super().__init__(parent, self.create_menu, stretch_column=self.Columns.NODE_ALIAS,
                         editable_columns=[])
        self.setModel(QtGui.QStandardItemModel(self))
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.main_window = parent
        self.gossip_db_loaded.connect(self.on_gossip_db)
        self.update_rows.connect(self.do_update_rows)
        self.update_single_row.connect(self.do_update_single_row)
        self.network = self.parent.network
        self.lnworker = self.parent.wallet.lnworker
        self.setSortingEnabled(True)

    def format_fields(self, chan: AbstractChannel) -> Dict['ChannelsList.Columns', str]:
        labels = {}
        for subject in (REMOTE, LOCAL):
            if isinstance(chan, Channel):
                can_send = chan.available_to_spend(subject) / 1000
                label = self.parent.format_amount(can_send)
                other = subject.inverted()
                bal_other = chan.balance(other)//1000
                bal_minus_htlcs_other = chan.balance_minus_outgoing_htlcs(other)//1000
                if bal_other != bal_minus_htlcs_other:
                    label += ' (+' + self.parent.format_amount(bal_other - bal_minus_htlcs_other) + ')'
            else:
                assert isinstance(chan, ChannelBackup)
                label = ''
            labels[subject] = label
        status = chan.get_state_for_GUI()
        closed = chan.is_closed()
        node_alias = self.lnworker.get_node_alias(chan.node_id) or chan.node_id.hex()
        capacity_str = self.parent.format_amount(chan.get_capacity(), whitespaces=True)
        return {
            self.Columns.SHORT_CHANID: chan.short_id_for_GUI(),
            self.Columns.NODE_ALIAS: node_alias,
            self.Columns.CAPACITY: capacity_str,
            self.Columns.LOCAL_BALANCE: '' if closed else labels[LOCAL],
            self.Columns.REMOTE_BALANCE: '' if closed else labels[REMOTE],
            self.Columns.CHANNEL_STATUS: status,
        }

    def on_success(self, txid):
        self.main_window.show_error('Channel closed' + '\n' + txid)

    def on_failure(self, exc_info):
        type_, e, tb = exc_info
        traceback.print_tb(tb)
        self.main_window.show_error('Failed to close channel:\n{}'.format(repr(e)))

    def close_channel(self, channel_id):
        self.is_force_close = False
        msg = _('Close channel?')
        force_cb = QCheckBox('Request force close from remote peer')
        tooltip = _(
            'If you check this option, your node will pretend that it has lost its data and ask the remote node to broadcast their latest state. '
            'Doing so from time to time helps make sure that nodes are honest, because your node can punish them if they broadcast a revoked state.')<commit_msg>(minor) word wrap qt tooltip<commit_after># -*- coding: utf-8 -*-
import traceback
from enum import IntEnum
from typing import Sequence, Optional, Dict

from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QMenu, QHBoxLayout, QLabel, QVBoxLayout, QGridLayout, QLineEdit,
                             QPushButton, QAbstractItemView, QComboBox, QCheckBox)
from PyQt5.QtGui import QFont, QStandardItem, QBrush

from electrum.util import bh2u, NotEnoughFunds, NoDynamicFeeEstimates
from electrum.i18n import _
from electrum.lnchannel import AbstractChannel, PeerState, ChannelBackup, Channel
from electrum.wallet import Abstract_Wallet
from electrum.lnutil import LOCAL, REMOTE, format_short_channel_id, LN_MAX_FUNDING_SAT
from electrum.lnworker import LNWallet

from .util import (MyTreeView, WindowModalDialog, Buttons, OkButton, CancelButton,
                   EnterButton, WaitingDialog, MONOSPACE_FONT, ColorScheme)
from .amountedit import BTCAmountEdit, FreezableLineEdit


ROLE_CHANNEL_ID = Qt.UserRole


class ChannelsList(MyTreeView):
    update_rows = QtCore.pyqtSignal(Abstract_Wallet)
    update_single_row = QtCore.pyqtSignal(Abstract_Wallet, AbstractChannel)
    gossip_db_loaded = QtCore.pyqtSignal()

    class Columns(IntEnum):
        SHORT_CHANID = 0
        NODE_ALIAS = 1
        CAPACITY = 2
        LOCAL_BALANCE = 3
        REMOTE_BALANCE = 4
        CHANNEL_STATUS = 5

    headers = {
        Columns.SHORT_CHANID: _('Short Channel ID'),
        Columns.NODE_ALIAS: _('Node alias'),
        Columns.CAPACITY: _('Capacity'),
        Columns.LOCAL_BALANCE: _('Can send'),
        Columns.REMOTE_BALANCE: _('Can receive'),
        Columns.CHANNEL_STATUS: _('Status'),
    }

    filter_columns = [
        Columns.SHORT_CHANID,
        Columns.NODE_ALIAS,
        Columns.CHANNEL_STATUS,
    ]

    _default_item_bg_brush = None  # type: Optional[QBrush]

    def __init__(self, parent):
        super().__init__(parent, self.create_menu, stretch_column=self.Columns.NODE_ALIAS,
                         editable_columns=[])
        self.setModel(QtGui.QStandardItemModel(self))
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.main_window = parent
        self.gossip_db_loaded.connect(self.on_gossip_db)
        self.update_rows.connect(self.do_update_rows)
        self.update_single_row.connect(self.do_update_single_row)
        self.network = self.parent.network
        self.lnworker = self.parent.wallet.lnworker
        self.setSortingEnabled(True)

    def format_fields(self, chan: AbstractChannel) -> Dict['ChannelsList.Columns', str]:
        labels = {}
        for subject in (REMOTE, LOCAL):
            if isinstance(chan, Channel):
                can_send = chan.available_to_spend(subject) / 1000
                label = self.parent.format_amount(can_send)
                other = subject.inverted()
                bal_other = chan.balance(other)//1000
                bal_minus_htlcs_other = chan.balance_minus_outgoing_htlcs(other)//1000
                if bal_other != bal_minus_htlcs_other:
                    label += ' (+' + self.parent.format_amount(bal_other - bal_minus_htlcs_other) + ')'
            else:
                assert isinstance(chan, ChannelBackup)
                label = ''
            labels[subject] = label
        status = chan.get_state_for_GUI()
        closed = chan.is_closed()
        node_alias = self.lnworker.get_node_alias(chan.node_id) or chan.node_id.hex()
        capacity_str = self.parent.format_amount(chan.get_capacity(), whitespaces=True)
        return {
            self.Columns.SHORT_CHANID: chan.short_id_for_GUI(),
            self.Columns.NODE_ALIAS: node_alias,
            self.Columns.CAPACITY: capacity_str,
            self.Columns.LOCAL_BALANCE: '' if closed else labels[LOCAL],
            self.Columns.REMOTE_BALANCE: '' if closed else labels[REMOTE],
            self.Columns.CHANNEL_STATUS: status,
        }

    def on_success(self, txid):
        self.main_window.show_error('Channel closed' + '\n' + txid)

    def on_failure(self, exc_info):
        type_, e, tb = exc_info
        traceback.print_tb(tb)
        self.main_window.show_error('Failed to close channel:\n{}'.format(repr(e)))

    def close_channel(self, channel_id):
        self.is_force_close = False
        msg = _('Close channel?')
        force_cb = QCheckBox('Request force close from remote peer')
        tooltip = _(
            'If you check this option, your node will pretend that it has lost its data and ask the remote node to broadcast their latest state. '
            'Doing so from time to time helps make sure that nodes are honest, because your node can punish them if they broadcast a revoked state.')
        tooltip = '<qt>' + tooltip + '</qt>' # rich text is word wrapped
{'fraction_non_alphanumeric': 0.08797745773324984, 'fraction_numerical': 0.005426841995408057, 'mean_word_length': 2.840881763527054, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7637290', 'n_tokens_mistral': 3006, 'n_tokens_neox': 2834, 'n_words': 642}