示例#1
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.api = WikipediaAPI(on_error=self.Error.api_error)
        self.result = None

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

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

        row = 0
        self.query_edit = ListEdit(self, 'query_list',
                                   "Each line represents a "
                                   "separate query.", 100, self)
        layout.addWidget(QLabel('Query word list:'), row, 0, 1,
                         self.label_width)
        layout.addWidget(self.query_edit, row, self.label_width, 1,
                         self.widgets_width)

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

        # Articles per query
        row += 1
        layout.addWidget(QLabel('Articles per query:'), row, 0, 1,
                         self.label_width)
        slider = gui.valueSlider(query_box,
                                 self,
                                 'articles_per_query',
                                 box='',
                                 values=[1, 3, 5, 10, 25])
        layout.addWidget(slider.box, row, 1, 1, self.widgets_width)

        query_box.layout().addLayout(layout)
        self.controlArea.layout().addWidget(query_box)

        self.controlArea.layout().addWidget(
            CheckListLayout('Text includes',
                            self,
                            'text_includes',
                            self.attributes,
                            cols=2,
                            callback=self.set_text_features))

        self.info_box = gui.hBox(self.controlArea, 'Info')
        self.result_label = gui.label(self.info_box, self,
                                      self.info_label.format(0))

        self.button_box = gui.hBox(self.controlArea)

        self.search_button = gui.button(self.button_box, self, 'Search',
                                        self.start_stop)
        self.search_button.setFocusPolicy(Qt.NoFocus)
示例#2
0
class OWTwitter(OWWidget, ConcurrentWidgetMixin):
    name = "Twitter"
    description = "Load tweets from the Twitter API."
    icon = "icons/Twitter.svg"
    keywords = ["twitter", "tweet"]
    priority = 150

    class Outputs:
        corpus = Output("Corpus", Corpus)

    want_main_area = False
    resizing_enabled = False

    class Info(OWWidget.Information):
        nut_enough_tweets = Msg(
            "Downloaded fewer tweets than requested, since not enough tweets or rate limit reached"
        )

    class Error(OWWidget.Error):
        api_error = Msg("Api error: {}")
        empty_query = Msg("Please provide {}.")
        key_missing = Msg("Please provide a valid API token.")
        wrong_author = Msg("Author '{}' does not exist.")

    CONTENT, AUTHOR = 0, 1
    MODES = ["Content", "Author"]
    word_list: List = Setting([])
    mode: int = Setting(0)
    limited_search: bool = Setting(True)
    max_tweets: int = Setting(100)
    language: Optional[str] = Setting(None)
    allow_retweets: bool = Setting(False)
    collecting: bool = Setting(False)

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

        def __init__(self, parent):
            super().__init__()
            self.cm_key = CredentialManager("Twitter Bearer Token")
            self.parent = parent

            box = gui.vBox(self.controlArea, "Bearer Token")
            self.key_edit = QPlainTextEdit()
            box.layout().addWidget(self.key_edit)

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

        def load_credentials(self):
            self.key_edit.setPlainText(self.cm_key.key)

        def save_credentials(self):
            self.cm_key.key = self.key_edit.toPlainText()

        def accept(self):
            token = self.key_edit.toPlainText()
            if token:
                self.save_credentials()
                self.parent.update_api(token)
                super().accept()

    def __init__(self):
        OWWidget.__init__(self)
        ConcurrentWidgetMixin.__init__(self)
        self.api = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept()

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

        # Query
        query_box = gui.hBox(self.controlArea, "Query")
        layout = QGridLayout()
        layout.setVerticalSpacing(5)
        layout.setColumnStretch(2, 1)  # stretch last columns
        layout.setColumnMinimumWidth(1, 15)  # add some space for checkbox
        ROW = 0
        COLUMNS = 3

        def add_row(label, items):
            nonlocal ROW, COLUMNS
            layout.addWidget(QLabel(label), ROW, 0)
            if isinstance(items, tuple):
                for i, item in enumerate(items):
                    layout.addWidget(item, ROW, 1 + i)
            else:
                layout.addWidget(items, ROW, 1, 1, COLUMNS - 1)
            ROW += 1

        # Query input
        add_row(
            "Query word list:",
            ListEdit(
                self,
                "word_list",
                "Multiple lines are joined with OR.",
                80,
                self,
            ),
        )

        # Search mode
        add_row(
            "Search by:",
            gui.comboBox(self,
                         self,
                         "mode",
                         items=self.MODES,
                         callback=self.mode_toggle),
        )

        # Language
        langs = (("Any", None), ) + tuple(
            (code2lang[l], l) for l in SUPPORTED_LANGUAGES)
        self.language_combo = ComboBox(self, "language", items=langs)
        add_row("Language:", self.language_combo)

        # Max tweets
        add_row(
            "Max tweets:",
            gui.spin(
                self,
                self,
                "max_tweets",
                minv=1,
                maxv=10000,
                checked="limited_search",
            ),
        )

        # Retweets
        self.retweets_checkbox = gui.checkBox(self,
                                              self,
                                              "allow_retweets",
                                              "",
                                              minimumHeight=30)
        add_row("Allow retweets:", self.retweets_checkbox)

        # Collect Results
        add_row("Collect results:", gui.checkBox(self, self, "collecting", ""))

        query_box.layout().addLayout(layout)

        # Buttons
        self.search_button = gui.button(
            self.buttonsArea,
            self,
            "Search",
            self.start_stop,
            focusPolicy=Qt.NoFocus,
        )

        self.mode_toggle()
        self.setFocus()  # to widget itself to show placeholder for query_edit

    def open_key_dialog(self):
        self.api_dlg.exec_()

    def mode_toggle(self):
        if self.mode == self.AUTHOR:
            self.language_combo.setCurrentIndex(0)
            self.retweets_checkbox.setCheckState(False)
        self.retweets_checkbox.setEnabled(self.mode == self.CONTENT)
        self.language_combo.setEnabled(self.mode == self.CONTENT)

    def start_stop(self):
        if self.task:
            self.cancel()
            self.search_button.setText("Search")
        else:
            self.run_search()

    @gui_require("api", "key_missing")
    def run_search(self):
        self.Error.clear()
        self.Info.nut_enough_tweets.clear()
        self.search()

    def search(self):
        max_tweets = self.max_tweets if self.limited_search else None
        content = self.mode == self.CONTENT
        if not self.word_list:
            self.Error.empty_query("keywords" if content else "authors")
            self.Outputs.corpus.send(None)
            return

        self.start(
            search,
            self.api,
            max_tweets,
            self.word_list,
            self.collecting,
            self.language if content else None,
            self.allow_retweets if content else None,
            "content" if content else "authors",
        )
        self.search_button.setText("Stop")

    def update_api(self, key):
        if key:
            self.Error.key_missing.clear()
            self.api = twitter.TwitterAPI(key)
        else:
            self.api = None

    def on_done(self, result_corpus):
        self.search_button.setText("Search")
        if (result_corpus is
                None  # probably because of rate error at beginning
                # or fewer tweets than expected
                or self.mode == self.CONTENT
                and len(result_corpus) < self.max_tweets or
                self.mode == self.AUTHOR
                # for authors, we expect self.max_tweets for each author
                and
                len(result_corpus) < self.max_tweets * len(self.word_list)):
            self.Info.nut_enough_tweets()
        self.Outputs.corpus.send(result_corpus)

    def on_exception(self, ex):
        self.search_button.setText("Search")
        if isinstance(ex, NoAuthorError):
            self.Error.wrong_author(str(ex))
        else:
            self.Error.api_error(str(ex))

    def on_partial_result(self, _):
        pass

    @gui_require("api", "key_missing")
    def send_report(self):
        for task in self.api.search_history:
            self.report_items(task)
示例#3
0
    def __init__(self):
        OWWidget.__init__(self)
        ConcurrentWidgetMixin.__init__(self)
        self.api = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept()

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

        # Query
        query_box = gui.hBox(self.controlArea, "Query")
        layout = QGridLayout()
        layout.setVerticalSpacing(5)
        layout.setColumnStretch(2, 1)  # stretch last columns
        layout.setColumnMinimumWidth(1, 15)  # add some space for checkbox
        ROW = 0
        COLUMNS = 3

        def add_row(label, items):
            nonlocal ROW, COLUMNS
            layout.addWidget(QLabel(label), ROW, 0)
            if isinstance(items, tuple):
                for i, item in enumerate(items):
                    layout.addWidget(item, ROW, 1 + i)
            else:
                layout.addWidget(items, ROW, 1, 1, COLUMNS - 1)
            ROW += 1

        # Query input
        add_row(
            "Query word list:",
            ListEdit(
                self,
                "word_list",
                "Multiple lines are joined with OR.",
                80,
                self,
            ),
        )

        # Search mode
        add_row(
            "Search by:",
            gui.comboBox(self,
                         self,
                         "mode",
                         items=self.MODES,
                         callback=self.mode_toggle),
        )

        # Language
        langs = (("Any", None), ) + tuple(
            (code2lang[l], l) for l in SUPPORTED_LANGUAGES)
        self.language_combo = ComboBox(self, "language", items=langs)
        add_row("Language:", self.language_combo)

        # Max tweets
        add_row(
            "Max tweets:",
            gui.spin(
                self,
                self,
                "max_tweets",
                minv=1,
                maxv=10000,
                checked="limited_search",
            ),
        )

        # Retweets
        self.retweets_checkbox = gui.checkBox(self,
                                              self,
                                              "allow_retweets",
                                              "",
                                              minimumHeight=30)
        add_row("Allow retweets:", self.retweets_checkbox)

        # Collect Results
        add_row("Collect results:", gui.checkBox(self, self, "collecting", ""))

        query_box.layout().addLayout(layout)

        # Buttons
        self.search_button = gui.button(
            self.buttonsArea,
            self,
            "Search",
            self.start_stop,
            focusPolicy=Qt.NoFocus,
        )

        self.mode_toggle()
        self.setFocus()  # to widget itself to show placeholder for query_edit
示例#4
0
class OWTwitter(OWWidget, ConcurrentWidgetMixin):
    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 = QFormLayout()
            form.setContentsMargins(5, 5, 5, 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 = 150

    class Outputs:
        corpus = Output("Corpus", Corpus)

    want_main_area = False
    resizing_enabled = False

    class Warning(OWWidget.Warning):
        no_text_fields = Msg("Text features are inferred when none selected.")

    class Error(OWWidget.Error):
        api_error = Msg("Api error ({})")
        rate_limit = Msg("Rate limit exceeded. Please try again later.")
        empty_authors = Msg("Please provide some authors.")
        wrong_authors = Msg("Query does not match Twitter user handle.")
        key_missing = Msg("Please provide a valid API key to get the data.")

    CONTENT, AUTHOR = 0, 1
    MODES = ["Content", "Author"]
    word_list = Setting([])
    mode = Setting(0)
    limited_search = Setting(True)
    max_tweets = Setting(100)
    language = Setting(None)
    allow_retweets = Setting(False)
    collecting = Setting(False)

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

    def __init__(self):
        OWWidget.__init__(self)
        ConcurrentWidgetMixin.__init__(self)
        self.api = None
        self.corpus = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept(silent=True)

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

        # Query
        query_box = gui.hBox(self.controlArea, "Query")
        layout = QGridLayout()
        layout.setVerticalSpacing(5)
        layout.setColumnStretch(2, 1)  # stretch last columns
        layout.setColumnMinimumWidth(1, 15)  # add some space for checkbox
        ROW = 0
        COLUMNS = 3

        def add_row(label, items):
            nonlocal ROW, COLUMNS
            layout.addWidget(QLabel(label), ROW, 0)
            if isinstance(items, tuple):
                for i, item in enumerate(items):
                    layout.addWidget(item, ROW, 1 + i)
            else:
                layout.addWidget(items, ROW, 1, 1, COLUMNS - 1)
            ROW += 1

        # Query input
        add_row(
            "Query word list:",
            ListEdit(
                self,
                "word_list",
                "Multiple lines are joined with OR.",
                80,
                self,
            ),
        )

        # Search mode
        add_row(
            "Search by:",
            gui.comboBox(self,
                         self,
                         "mode",
                         items=self.MODES,
                         callback=self.mode_toggle),
        )

        # Language
        self.language_combo = ComboBox(
            self,
            "language",
            items=(("Any", None), ) + tuple(sorted(lang2code.items())),
        )
        add_row("Language:", self.language_combo)

        # Max tweets
        add_row(
            "Max tweets:",
            gui.spin(
                self,
                self,
                "max_tweets",
                minv=1,
                maxv=10000,
                checked="limited_search",
            ),
        )

        # Retweets
        self.retweets_checkbox = gui.checkBox(self,
                                              self,
                                              "allow_retweets",
                                              "",
                                              minimumHeight=30)
        add_row("Allow retweets:", self.retweets_checkbox)

        # Collect Results
        add_row("Collect results:", gui.checkBox(self, self, "collecting", ""))

        query_box.layout().addLayout(layout)

        self.controlArea.layout().addWidget(
            CheckListLayout(
                "Text includes",
                self,
                "text_includes",
                self.attributes,
                cols=2,
                callback=self.set_text_features,
            ))

        # Buttons
        self.button_box = gui.hBox(self.controlArea)
        self.search_button = gui.button(
            self.button_box,
            self,
            "Search",
            self.start_stop,
            focusPolicy=Qt.NoFocus,
        )

        self.mode_toggle()
        self.setFocus()  # to widget itself to show placeholder for query_edit

    def open_key_dialog(self):
        self.api_dlg.exec_()

    def mode_toggle(self):
        if self.mode == self.AUTHOR:
            self.language_combo.setCurrentIndex(0)
            self.retweets_checkbox.setCheckState(False)
        self.retweets_checkbox.setEnabled(self.mode == self.CONTENT)
        self.language_combo.setEnabled(self.mode == self.CONTENT)

    def start_stop(self):
        if self.task:
            self.cancel()
            self.search_button.setText("Search")
        else:
            self.run_search()

    @gui_require("api", "key_missing")
    def run_search(self):
        self.Error.clear()
        self.search()

    def search(self):
        max_tweets = self.max_tweets if self.limited_search else 0

        if self.mode == self.CONTENT:
            self.start(
                search,
                self.api,
                max_tweets,
                self.word_list,
                self.collecting,
                self.language,
                self.allow_retweets,
                "content",
            )
        else:
            if not self.word_list:
                self.Error.empty_authors()
                return None
            if not any(a.startswith("@") for a in self.word_list):
                self.Error.wrong_authors()
                return None
            self.start(
                search,
                self.api,
                max_tweets,
                self.word_list,
                self.collecting,
                None,
                None,
                "authors",
            )
        self.search_button.setText("Stop")

    def update_api(self, key):
        if key:
            self.Error.key_missing.clear()
            self.api = twitter.TwitterAPI(key)
        else:
            self.api = None

    def on_done(self, result):
        self.search_button.setText("Search")
        if result:
            self.info.set_output_summary(len(result),
                                         f"{len(result)} tweets on output")
        else:
            self.info.set_output_summary(self.info.NoOutput)
        self.corpus = result
        self.set_text_features()

    def on_exception(self, ex):
        self.search_button.setText("Search")
        if isinstance(ex, TweepError) and ex.response.status_code == 429:
            self.Error.rate_limit()
        else:
            self.Error.api_error(str(ex))

    def on_partial_result(self, _):
        pass

    def set_text_features(self):
        self.Warning.no_text_fields.clear()
        if not self.text_includes:
            self.Warning.no_text_fields()

        if self.corpus is not None:
            vars_ = [
                var for var in self.corpus.domain.metas
                if var.name in self.text_includes
            ]
            self.corpus.set_text_features(vars_ or None)
            self.Outputs.corpus.send(self.corpus)

    @gui_require("api", "key_missing")
    def send_report(self):
        for task in self.api.search_history:
            self.report_items(task)
示例#5
0
class OWTwitter(OWConcurrentWidget):
    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 = QFormLayout()
            form.setContentsMargins(5, 5, 5, 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

    class Warning(OWWidget.Warning):
        no_text_fields = Msg('Text features are inferred when none selected.')

    class Error(OWWidget.Error):
        api = Msg('Api error ({})')
        rate_limit = Msg('Rate limit exceeded. Please try again later.')
        empty_authors = Msg('Please provide some authors.')
        key_missing = Msg('Please provide a valid API key to get the data.')

    tweets_info = 'Tweets on output: {}'

    CONTENT, AUTHOR = 0, 1
    MODES = ['Content', 'Author']
    word_list = Setting([])
    mode = Setting(0)
    limited_search = Setting(True)
    max_tweets = Setting(100)
    language = Setting(None)
    allow_retweets = Setting(False)
    collecting = Setting(False)

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

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.api = None
        self.corpus = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept(silent=True)

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

        # Query
        query_box = gui.hBox(self.controlArea, 'Query')
        layout = QGridLayout()
        layout.setVerticalSpacing(5)
        layout.setColumnStretch(2, 1)           # stretch last columns
        layout.setColumnMinimumWidth(1, 15)     # add some space for checkbox
        ROW = 0
        COLUMNS = 3

        def add_row(label, items):
            nonlocal ROW, COLUMNS
            layout.addWidget(QLabel(label), ROW, 0)
            if isinstance(items, tuple):
                for i, item in enumerate(items):
                    layout.addWidget(item, ROW, 1+i)
            else:
                layout.addWidget(items, ROW, 1, 1, COLUMNS-1)
            ROW += 1

        # Query input
        add_row('Query word list:',
                ListEdit(self, 'word_list',
                         'Multiple lines are joined with OR.', 80, self))

        # Search mode
        add_row('Search by:',
                gui.comboBox(self, self, 'mode', items=self.MODES,
                             callback=self.mode_toggle))

        # Language
        self.language_combo = ComboBox(self, 'language',
                                       items=(('Any', None),) +
                                             tuple(sorted(lang2code.items())))
        add_row('Language:', self.language_combo)

        # Max tweets
        add_row('Max tweets:',
                gui.spin(self, self, 'max_tweets', minv=1, maxv=10000,
                         checked='limited_search'))

        # Retweets
        self.retweets_checkbox = gui.checkBox(self, self, 'allow_retweets', '', minimumHeight=30)
        add_row('Allow retweets:', self.retweets_checkbox)

        # Collect Results
        add_row('Collect results:',
                gui.checkBox(self, self, 'collecting', ''))

        query_box.layout().addLayout(layout)

        self.controlArea.layout().addWidget(
            CheckListLayout('Text includes', self, 'text_includes',
                            self.attributes, cols=2,
                            callback=self.set_text_features))

        self.tweets_info_label = gui.label(self.controlArea, self,
                                           self.tweets_info.format(0),
                                           box='Info')

        # Buttons
        self.button_box = gui.hBox(self.controlArea)
        self.button_box.layout().addWidget(self.report_button)

        self.search_button = gui.button(self.button_box, self, 'Search',
                                        self.start_stop,
                                        focusPolicy=Qt.NoFocus)

        self.mode_toggle()
        self.setFocus()  # to widget itself to show placeholder for query_edit

    def open_key_dialog(self):
        self.api_dlg.exec_()

    def mode_toggle(self):
        if self.mode == self.AUTHOR:
            self.language_combo.setCurrentIndex(0)
            self.retweets_checkbox.setCheckState(False)
        self.retweets_checkbox.setEnabled(self.mode == self.CONTENT)
        self.language_combo.setEnabled(self.mode == self.CONTENT)

    def start_stop(self):
        if self.running:
            self.stop()
        else:
            self.search()

    def update_api(self, key):
        if key:
            self.Error.key_missing.clear()
            self.api = twitter.TwitterAPI(key,
                                          on_error=self.Error.api,
                                          on_rate_limit=self.Error.rate_limit)
        else:
            self.api = None

    @gui_require('api', 'key_missing')
    @asynchronous(allow_partial_results=True)
    def search(self, on_progress, should_break):
        def progress_with_info(total, progress):
            if self.limited_search or self.mode == self.AUTHOR:
                on_progress(100 * progress)
            self.update_tweets_num(total)

        self.Error.clear()
        self.api.on_progress = progress_with_info
        self.api.should_break = should_break
        max_tweets = self.max_tweets if self.limited_search else 0

        if self.mode == self.CONTENT:
            return self.api.search_content(max_tweets=max_tweets,
                                           content=self.word_list,
                                           lang=self.language,
                                           allow_retweets=self.allow_retweets,
                                           collecting=self.collecting)
        else:
            if not self.word_list:
                self.Error.empty_authors()
                return None
            return self.api.search_authors(max_tweets=max_tweets,
                                           authors=self.word_list,
                                           collecting=self.collecting)

    def on_start(self):
        self.search_button.setText('Stop')
        self.send(IO.CORPUS, None)
        if self.mode == self.CONTENT and not self.limited_search:
            self.progressBarFinished(None)

    def on_result(self, result):
        self.search_button.setText('Search')
        self.update_tweets_num(len(result) if result else 0)
        self.corpus = result
        self.set_text_features()

    def update_tweets_num(self, num=0):
        self.tweets_info_label.setText(self.tweets_info.format(num))

    def set_text_features(self):
        self.Warning.no_text_fields.clear()
        if not self.text_includes:
            self.Warning.no_text_fields()

        if self.corpus is not None:
            vars_ = [var for var in self.corpus.domain.metas
                     if var.name in self.text_includes]
            self.corpus.set_text_features(vars_ or None)
            self.send(IO.CORPUS, self.corpus)

    @gui_require('api', 'key_missing')
    def send_report(self):
        for task in self.api.search_history:
            self.report_items(task)
示例#6
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.api = None
        self.corpus = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept(silent=True)

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

        # Query
        query_box = gui.hBox(self.controlArea, 'Query')
        layout = QGridLayout()
        layout.setVerticalSpacing(5)
        layout.setColumnStretch(2, 1)           # stretch last columns
        layout.setColumnMinimumWidth(1, 15)     # add some space for checkbox
        ROW = 0
        COLUMNS = 3

        def add_row(label, items):
            nonlocal ROW, COLUMNS
            layout.addWidget(QLabel(label), ROW, 0)
            if isinstance(items, tuple):
                for i, item in enumerate(items):
                    layout.addWidget(item, ROW, 1+i)
            else:
                layout.addWidget(items, ROW, 1, 1, COLUMNS-1)
            ROW += 1

        # Query input
        add_row('Query word list:',
                ListEdit(self, 'word_list',
                         'Multiple lines are joined with OR.', 80, self))

        # Search mode
        add_row('Search by:',
                gui.comboBox(self, self, 'mode', items=self.MODES,
                             callback=self.mode_toggle))

        # Language
        self.language_combo = ComboBox(self, 'language',
                                       items=(('Any', None),) +
                                             tuple(sorted(lang2code.items())))
        add_row('Language:', self.language_combo)

        # Max tweets
        add_row('Max tweets:',
                gui.spin(self, self, 'max_tweets', minv=1, maxv=10000,
                         checked='limited_search'))

        # Retweets
        self.retweets_checkbox = gui.checkBox(self, self, 'allow_retweets', '', minimumHeight=30)
        add_row('Allow retweets:', self.retweets_checkbox)

        # Collect Results
        add_row('Collect results:',
                gui.checkBox(self, self, 'collecting', ''))

        query_box.layout().addLayout(layout)

        self.controlArea.layout().addWidget(
            CheckListLayout('Text includes', self, 'text_includes',
                            self.attributes, cols=2,
                            callback=self.set_text_features))

        self.tweets_info_label = gui.label(self.controlArea, self,
                                           self.tweets_info.format(0),
                                           box='Info')

        # Buttons
        self.button_box = gui.hBox(self.controlArea)
        self.button_box.layout().addWidget(self.report_button)

        self.search_button = gui.button(self.button_box, self, 'Search',
                                        self.start_stop,
                                        focusPolicy=Qt.NoFocus)

        self.mode_toggle()
        self.setFocus()  # to widget itself to show placeholder for query_edit
示例#7
0
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 = QFormLayout()
            form.setContentsMargins(5, 5, 5, 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 = 150

    class Outputs:
        corpus = Output("Corpus", Corpus)

    want_main_area = False
    resizing_enabled = False

    class Warning(OWWidget.Warning):
        no_text_fields = Msg('Text features are inferred when none selected.')

    class Error(OWWidget.Error):
        api = Msg('Api error ({})')
        rate_limit = Msg('Rate limit exceeded. Please try again later.')
        empty_authors = Msg('Please provide some authors.')
        key_missing = Msg('Please provide a valid API key to get the data.')

    tweets_info = 'Tweets on output: {}'

    CONTENT, AUTHOR = 0, 1
    MODES = ['Content', 'Author']
    word_list = Setting([])
    mode = Setting(0)
    limited_search = Setting(True)
    max_tweets = Setting(100)
    language = Setting(None)
    allow_retweets = Setting(False)
    collecting = Setting(False)

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

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.api = None
        self.corpus = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept(silent=True)

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

        # Query
        query_box = gui.hBox(self.controlArea, 'Query')
        layout = QGridLayout()
        layout.setVerticalSpacing(5)
        layout.setColumnStretch(2, 1)  # stretch last columns
        layout.setColumnMinimumWidth(1, 15)  # add some space for checkbox
        ROW = 0
        COLUMNS = 3

        def add_row(label, items):
            nonlocal ROW, COLUMNS
            layout.addWidget(QLabel(label), ROW, 0)
            if isinstance(items, tuple):
                for i, item in enumerate(items):
                    layout.addWidget(item, ROW, 1 + i)
            else:
                layout.addWidget(items, ROW, 1, 1, COLUMNS - 1)
            ROW += 1

        # Query input
        add_row(
            'Query word list:',
            ListEdit(self, 'word_list', 'Multiple lines are joined with OR.',
                     80, self))

        # Search mode
        add_row(
            'Search by:',
            gui.comboBox(self,
                         self,
                         'mode',
                         items=self.MODES,
                         callback=self.mode_toggle))

        # Language
        self.language_combo = ComboBox(self,
                                       'language',
                                       items=(('Any', None), ) +
                                       tuple(sorted(lang2code.items())))
        add_row('Language:', self.language_combo)

        # Max tweets
        add_row(
            'Max tweets:',
            gui.spin(self,
                     self,
                     'max_tweets',
                     minv=1,
                     maxv=10000,
                     checked='limited_search'))

        # Retweets
        self.retweets_checkbox = gui.checkBox(self,
                                              self,
                                              'allow_retweets',
                                              '',
                                              minimumHeight=30)
        add_row('Allow retweets:', self.retweets_checkbox)

        # Collect Results
        add_row('Collect results:', gui.checkBox(self, self, 'collecting', ''))

        query_box.layout().addLayout(layout)

        self.controlArea.layout().addWidget(
            CheckListLayout('Text includes',
                            self,
                            'text_includes',
                            self.attributes,
                            cols=2,
                            callback=self.set_text_features))

        self.tweets_info_label = gui.label(self.controlArea,
                                           self,
                                           self.tweets_info.format(0),
                                           box='Info')

        # Buttons
        self.button_box = gui.hBox(self.controlArea)
        self.button_box.layout().addWidget(self.report_button)

        self.search_button = gui.button(self.button_box,
                                        self,
                                        'Search',
                                        self.start_stop,
                                        focusPolicy=Qt.NoFocus)

        self.mode_toggle()
        self.setFocus()  # to widget itself to show placeholder for query_edit

    def open_key_dialog(self):
        self.api_dlg.exec_()

    def mode_toggle(self):
        if self.mode == self.AUTHOR:
            self.language_combo.setCurrentIndex(0)
            self.retweets_checkbox.setCheckState(False)
        self.retweets_checkbox.setEnabled(self.mode == self.CONTENT)
        self.language_combo.setEnabled(self.mode == self.CONTENT)

    def start_stop(self):
        if self.search.running:
            self.search.stop()
        else:
            self.run_search()

    @gui_require('api', 'key_missing')
    def run_search(self):
        self.search()

    @asynchronous
    def search(self):
        max_tweets = self.max_tweets if self.limited_search else 0

        if self.mode == self.CONTENT:
            return self.api.search_content(max_tweets=max_tweets,
                                           content=self.word_list,
                                           lang=self.language,
                                           allow_retweets=self.allow_retweets,
                                           collecting=self.collecting)
        else:
            if not self.word_list:
                self.Error.empty_authors()
                return None
            return self.api.search_authors(max_tweets=max_tweets,
                                           authors=self.word_list,
                                           collecting=self.collecting)

    def update_api(self, key):
        if key:
            self.Error.key_missing.clear()
            self.api = twitter.TwitterAPI(
                key,
                on_error=self.Error.api,
                on_rate_limit=self.Error.rate_limit,
                should_break=self.search.should_break,
                on_progress=self.update_tweets_num)
        else:
            self.api = None

    @search.on_start
    def on_start(self):
        self.Error.clear()
        self.progressBarInit(None)
        self.search_button.setText('Stop')
        self.Outputs.corpus.send(None)
        if self.mode == self.CONTENT and not self.limited_search:
            self.progressBarFinished(None)

    @search.on_result
    def on_result(self, result):
        self.search_button.setText('Search')
        self.tweets_info_label.setText(
            self.tweets_info.format(len(result) if result else 0))
        self.corpus = result
        self.set_text_features()
        self.progressBarFinished(None)

    @search.callback(should_raise=False)
    def update_tweets_num(self, num=0, progress=None):
        if self.limited_search or self.mode == self.AUTHOR:
            if progress is not None:
                self.progressBarSet(100 * progress, None)
        self.tweets_info_label.setText(self.tweets_info.format(num))

    def set_text_features(self):
        self.Warning.no_text_fields.clear()
        if not self.text_includes:
            self.Warning.no_text_fields()

        if self.corpus is not None:
            vars_ = [
                var for var in self.corpus.domain.metas
                if var.name in self.text_includes
            ]
            self.corpus.set_text_features(vars_ or None)
            self.Outputs.corpus.send(self.corpus)

    @gui_require('api', 'key_missing')
    def send_report(self):
        for task in self.api.search_history:
            self.report_items(task)
示例#8
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.api = None
        self.corpus = None
        self.api_dlg = self.APICredentialsDialog(self)
        self.api_dlg.accept(silent=True)

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

        # Query
        query_box = gui.hBox(self.controlArea, 'Query')
        layout = QGridLayout()
        layout.setVerticalSpacing(5)
        layout.setColumnStretch(2, 1)  # stretch last columns
        layout.setColumnMinimumWidth(1, 15)  # add some space for checkbox
        ROW = 0
        COLUMNS = 3

        def add_row(label, items):
            nonlocal ROW, COLUMNS
            layout.addWidget(QLabel(label), ROW, 0)
            if isinstance(items, tuple):
                for i, item in enumerate(items):
                    layout.addWidget(item, ROW, 1 + i)
            else:
                layout.addWidget(items, ROW, 1, 1, COLUMNS - 1)
            ROW += 1

        # Query input
        add_row(
            'Query word list:',
            ListEdit(self, 'word_list', 'Multiple lines are joined with OR.',
                     80, self))

        # Search mode
        add_row(
            'Search by:',
            gui.comboBox(self,
                         self,
                         'mode',
                         items=self.MODES,
                         callback=self.mode_toggle))

        # Language
        self.language_combo = ComboBox(self,
                                       'language',
                                       items=(('Any', None), ) +
                                       tuple(sorted(lang2code.items())))
        add_row('Language:', self.language_combo)

        # Max tweets
        add_row(
            'Max tweets:',
            gui.spin(self,
                     self,
                     'max_tweets',
                     minv=1,
                     maxv=10000,
                     checked='limited_search'))

        # Retweets
        self.retweets_checkbox = gui.checkBox(self,
                                              self,
                                              'allow_retweets',
                                              '',
                                              minimumHeight=30)
        add_row('Allow retweets:', self.retweets_checkbox)

        # Collect Results
        add_row('Collect results:', gui.checkBox(self, self, 'collecting', ''))

        query_box.layout().addLayout(layout)

        self.controlArea.layout().addWidget(
            CheckListLayout('Text includes',
                            self,
                            'text_includes',
                            self.attributes,
                            cols=2,
                            callback=self.set_text_features))

        self.tweets_info_label = gui.label(self.controlArea,
                                           self,
                                           self.tweets_info.format(0),
                                           box='Info')

        # Buttons
        self.button_box = gui.hBox(self.controlArea)
        self.button_box.layout().addWidget(self.report_button)

        self.search_button = gui.button(self.button_box,
                                        self,
                                        'Search',
                                        self.start_stop,
                                        focusPolicy=Qt.NoFocus)

        self.mode_toggle()
        self.setFocus()  # to widget itself to show placeholder for query_edit