<commit_before>from PyQt4 import QtGui, QtCore

from Orange.widgets import gui
from Orange.widgets.settings import Setting
from Orange.widgets.credentials import CredentialManager
from Orange.widgets.widget import OWWidget, Msg

from orangecontrib.text import twitter
from orangecontrib.text.corpus import Corpus
from orangecontrib.text.language_codes import lang2code
from orangecontrib.text.widgets.utils import (ComboBox, ListEdit,
                                              CheckListLayout, gui_require)


class IO:
    CORPUS = "Corpus"


class OWTwitter(OWWidget):
    class APICredentialsDialog(OWWidget):
        name = "Twitter API Credentials"
        want_main_area = False
        resizing_enabled = False

        cm_key = CredentialManager('Twitter API Key')
        cm_secret = CredentialManager('Twitter API Secret')

        key_input = ''
        secret_input = ''

        class Error(OWWidget.Error):
            invalid_credentials = Msg('This credentials are invalid.')

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

            form = QtGui.QFormLayout()
            form.setMargin(5)
            self.key_edit = gui.lineEdit(self, self, 'key_input', controlWidth=400)
            form.addRow('Key:', self.key_edit)
            self.secret_edit = gui.lineEdit(self, self, 'secret_input', controlWidth=400)
            form.addRow('Secret:', self.secret_edit)
            self.controlArea.layout().addLayout(form)

            self.submit_button = gui.button(self.controlArea, self, "OK", self.accept)

            self.load_credentials()

        def load_credentials(self):
            self.key_edit.setText(self.cm_key.key)
            self.secret_edit.setText(self.cm_secret.key)

        def save_credentials(self):
            self.cm_key.key = self.key_input
            self.cm_secret.key = self.secret_input

        def check_credentials(self):
            c = twitter.Credentials(self.key_input, self.secret_input)
            if self.credentials != c:
                if c.valid:
                    self.save_credentials()
                else:
                    c = None
                self.credentials = c

        def accept(self, silent=False):
            if not silent: self.Error.invalid_credentials.clear()
            self.check_credentials()
            if self.credentials and self.credentials.valid:
                self.parent.update_api(self.credentials)
                super().accept()
            elif not silent:
                self.Error.invalid_credentials()

    name = "Twitter"
    description = "Load tweets from the Twitter API."
    icon = "icons/Twitter.svg"
    priority = 25

    outputs = [(IO.CORPUS, Corpus)]
    want_main_area = False
    resizing_enabled = False
    widgets_width = 11
    label_width = 2
    text_area_height = 80

    class Warning(OWWidget.Warning):
        missed_key = Msg('Please provide a valid API key in order to get the data.')
        no_text_fields = Msg('Text features are inferred when none are selected.')

    MISSED_KEY = 'missed_key'

    class Error(OWWidget.Error):
        api = Msg('Api error ({})')
        rate_limit = Msg('Rate limit exceeded. Please try again later.')

    tweets_info = 'Tweets on output: {}'

    CONTENT, AUTHOR, CONTENT_AUTHOR = 0, 1, 2
    word_list = Setting([])
    mode = Setting(0)
    limited_search = Setting(True)
    max_tweets = Setting(100)
    language = Setting(None)
    allow_retweets = Setting(False)
    advance = Setting(False)
    include = Setting(False)
    includeON = Setting(True)

    error_signal = QtCore.pyqtSignal(str)
    finish_signal = QtCore.pyqtSignal()
    progress_signal = QtCore.pyqtSignal(int)
    start_signal = QtCore.pyqtSignal()

    attributes = [feat.name for feat in twitter.TwitterAPI.string_attributes]
    text_includes = Setting([feat.name for feat in twitter.TwitterAPI.text_features])

    def __init__(self, *args, **kwargs):
        """
        Attributes:
            api (twitter.TwitterAPI): Twitter API object.
        """
        super().__init__(*args, **kwargs)
        self.api = None
        self.corpus = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept(silent=True)

        self.controlArea.layout().setContentsMargins(0, 15, 0, 0)

        # Set API key button.
        key_dialog_button = gui.button(self.controlArea, self, 'Twitter API Key',
                                       callback=self.open_key_dialog,
                                       tooltip="Set the API key for this widget.")
        key_dialog_button.setFocusPolicy(QtCore.Qt.NoFocus)

        # Query
        self.query_box = gui.hBox(self.controlArea, 'Query')

        # Queries configuration
        layout = QtGui.QGridLayout()
        layout.setSpacing(7)

        # Query
        row = 0
        query_edit = ListEdit(self, 'word_list', "Multiple lines are automatically joined with OR.", self)
        query_edit.setFixedHeight(self.text_area_height)
        layout.addWidget(QtGui.QLabel('Query word list:'), row, 0, 1, self.label_width)
        layout.addWidget(query_edit, row, self.label_width, 1, self.widgets_width)

        # Search mode
        row += 1
        mode = gui.comboBox(self, self, 'mode')
        mode.addItem('Content')
        mode.addItem('Author')
        mode.addItem('Content & Author')
        layout.addWidget(QtGui.QLabel('Search by:'), row, 0, 1, self.label_width)
        layout.addWidget(mode, row, self.label_width, 1, self.widgets_width)

        # Language
        row += 1
        language_edit = ComboBox(self, 'language',
                                 (('Any', None),) + tuple(
                                     sorted(lang2code.items())))
        layout.addWidget(QtGui.QLabel('Language:'), row, 0, 1,
                         self.label_width)
        layout.addWidget(language_edit, row, self.label_width, 1,
                         self.widgets_width)

        # Retweets
        row += 1
        check = gui.checkBox(self, self, 'allow_retweets', '')
        layout.addWidget(QtGui.QLabel('Allow\nretweets:'), row, 0, 1, self.label_width)
        layout.addWidget(check, row, self.label_width, 1, 1)

        # Max tweets
        row += 1
        check, spin = gui.spin(self, self, 'max_tweets', minv=1, maxv=10000,
                               checked='limited_search')
        layout.addWidget(QtGui.QLabel('Max tweets:'), row, 0, 1, self.label_width)
        layout.addWidget(check, row, self.label_width, 1, 1)
        layout.addWidget(spin, row, self.label_width + 1, 1, self.widgets_width - 1)

        # Checkbox
        row += 1
        check = gui.checkBox(self, self, 'advance', '')
        layout.addWidget(QtGui.QLabel('Accumulate\nresults:'), row, 0, 1, self.label_width)
        layout.addWidget(check, row, self.label_width, 1, 1)
        self.query_box.layout().addLayout(layout)<commit_msg>OWTwitter: Move max_tweets up the layout<commit_after>from PyQt4 import QtGui, QtCore

from Orange.widgets import gui
from Orange.widgets.settings import Setting
from Orange.widgets.credentials import CredentialManager
from Orange.widgets.widget import OWWidget, Msg

from orangecontrib.text import twitter
from orangecontrib.text.corpus import Corpus
from orangecontrib.text.language_codes import lang2code
from orangecontrib.text.widgets.utils import (ComboBox, ListEdit,
                                              CheckListLayout, gui_require)


class IO:
    CORPUS = "Corpus"


class OWTwitter(OWWidget):
    class APICredentialsDialog(OWWidget):
        name = "Twitter API Credentials"
        want_main_area = False
        resizing_enabled = False

        cm_key = CredentialManager('Twitter API Key')
        cm_secret = CredentialManager('Twitter API Secret')

        key_input = ''
        secret_input = ''

        class Error(OWWidget.Error):
            invalid_credentials = Msg('This credentials are invalid.')

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

            form = QtGui.QFormLayout()
            form.setMargin(5)
            self.key_edit = gui.lineEdit(self, self, 'key_input', controlWidth=400)
            form.addRow('Key:', self.key_edit)
            self.secret_edit = gui.lineEdit(self, self, 'secret_input', controlWidth=400)
            form.addRow('Secret:', self.secret_edit)
            self.controlArea.layout().addLayout(form)

            self.submit_button = gui.button(self.controlArea, self, "OK", self.accept)

            self.load_credentials()

        def load_credentials(self):
            self.key_edit.setText(self.cm_key.key)
            self.secret_edit.setText(self.cm_secret.key)

        def save_credentials(self):
            self.cm_key.key = self.key_input
            self.cm_secret.key = self.secret_input

        def check_credentials(self):
            c = twitter.Credentials(self.key_input, self.secret_input)
            if self.credentials != c:
                if c.valid:
                    self.save_credentials()
                else:
                    c = None
                self.credentials = c

        def accept(self, silent=False):
            if not silent: self.Error.invalid_credentials.clear()
            self.check_credentials()
            if self.credentials and self.credentials.valid:
                self.parent.update_api(self.credentials)
                super().accept()
            elif not silent:
                self.Error.invalid_credentials()

    name = "Twitter"
    description = "Load tweets from the Twitter API."
    icon = "icons/Twitter.svg"
    priority = 25

    outputs = [(IO.CORPUS, Corpus)]
    want_main_area = False
    resizing_enabled = False
    widgets_width = 11
    label_width = 2
    text_area_height = 80

    class Warning(OWWidget.Warning):
        missed_key = Msg('Please provide a valid API key in order to get the data.')
        no_text_fields = Msg('Text features are inferred when none are selected.')

    MISSED_KEY = 'missed_key'

    class Error(OWWidget.Error):
        api = Msg('Api error ({})')
        rate_limit = Msg('Rate limit exceeded. Please try again later.')

    tweets_info = 'Tweets on output: {}'

    CONTENT, AUTHOR, CONTENT_AUTHOR = 0, 1, 2
    word_list = Setting([])
    mode = Setting(0)
    limited_search = Setting(True)
    max_tweets = Setting(100)
    language = Setting(None)
    allow_retweets = Setting(False)
    advance = Setting(False)
    include = Setting(False)
    includeON = Setting(True)

    error_signal = QtCore.pyqtSignal(str)
    finish_signal = QtCore.pyqtSignal()
    progress_signal = QtCore.pyqtSignal(int)
    start_signal = QtCore.pyqtSignal()

    attributes = [feat.name for feat in twitter.TwitterAPI.string_attributes]
    text_includes = Setting([feat.name for feat in twitter.TwitterAPI.text_features])

    def __init__(self, *args, **kwargs):
        """
        Attributes:
            api (twitter.TwitterAPI): Twitter API object.
        """
        super().__init__(*args, **kwargs)
        self.api = None
        self.corpus = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept(silent=True)

        self.controlArea.layout().setContentsMargins(0, 15, 0, 0)

        # Set API key button.
        key_dialog_button = gui.button(self.controlArea, self, 'Twitter API Key',
                                       callback=self.open_key_dialog,
                                       tooltip="Set the API key for this widget.")
        key_dialog_button.setFocusPolicy(QtCore.Qt.NoFocus)

        # Query
        self.query_box = gui.hBox(self.controlArea, 'Query')

        # Queries configuration
        layout = QtGui.QGridLayout()
        layout.setSpacing(7)

        # Query
        row = 0
        query_edit = ListEdit(self, 'word_list', "Multiple lines are automatically joined with OR.", self)
        query_edit.setFixedHeight(self.text_area_height)
        layout.addWidget(QtGui.QLabel('Query word list:'), row, 0, 1, self.label_width)
        layout.addWidget(query_edit, row, self.label_width, 1, self.widgets_width)

        # Search mode
        row += 1
        mode = gui.comboBox(self, self, 'mode')
        mode.addItem('Content')
        mode.addItem('Author')
        mode.addItem('Content & Author')
        layout.addWidget(QtGui.QLabel('Search by:'), row, 0, 1, self.label_width)
        layout.addWidget(mode, row, self.label_width, 1, self.widgets_width)

        # Language
        row += 1
        language_edit = ComboBox(self, 'language',
                                 (('Any', None),) + tuple(
                                     sorted(lang2code.items())))
        layout.addWidget(QtGui.QLabel('Language:'), row, 0, 1,
                         self.label_width)
        layout.addWidget(language_edit, row, self.label_width, 1,
                         self.widgets_width)

        # Max tweets
        row += 1
        check, spin = gui.spin(self, self, 'max_tweets', minv=1, maxv=10000,
                               checked='limited_search')
        layout.addWidget(QtGui.QLabel('Max tweets:'), row, 0, 1,
                         self.label_width)
        layout.addWidget(check, row, self.label_width, 1, 1)
        layout.addWidget(spin, row, self.label_width + 1, 1,
                         self.widgets_width - 1)

        # Retweets
        row += 1
        check = gui.checkBox(self, self, 'allow_retweets', '')
        layout.addWidget(QtGui.QLabel('Allow\nretweets:'), row, 0, 1, self.label_width)
        layout.addWidget(check, row, self.label_width, 1, 1)

        # Checkbox
        row += 1
        check = gui.checkBox(self, self, 'advance', '')
        layout.addWidget(QtGui.QLabel('Accumulate\nresults:'), row, 0, 1, self.label_width)
        layout.addWidget(check, row, self.label_width, 1, 1)
        self.query_box.layout().addLayout(layout)
{'fraction_non_alphanumeric': 0.10412657686551208, 'fraction_numerical': 0.009407740004276246, 'mean_word_length': 2.239150507848569, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6069250', 'n_tokens_mistral': 4338, 'n_tokens_neox': 4038, 'n_words': 902}