mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2024-11-22 19:28:09 -06:00
Merge pull request #486 from MCV-Software/autocomplete_redesign
Changes in user autocompletion feature. Closes #466. Closes #368
This commit is contained in:
commit
1e686cffba
@ -2,6 +2,9 @@ TWBlue Changelog
|
|||||||
|
|
||||||
## changes in this version
|
## changes in this version
|
||||||
|
|
||||||
|
* the user autocompletion feature has been completely rewritten to be easier to use, particularly for people with many followers/following users:
|
||||||
|
* In the account settings dialog, there's a button that opens up a new dialog that allows you to "scan" your account in order to add all users from your followers/following list. This process will read your data directly from Twitter and depending in the amount of people you have in your account it might take too many API calls. Please use it with caution. You can, for example, do the process separately for your followers/following people so it will be easier to handle, in case you have a massive amount of people. If TWBlue is unable to complete the scan, you will see an error and will be prompted to try again in 15 minutes, once your API calls have refreshed.
|
||||||
|
* It is possible to use the user autocompletion functionality in dialogs where you can select an user, for example when adding or removing someone from a list, or displaying lists for someone.
|
||||||
* Implemented a new setting, available in the account settings dialog, that allows to hide emojis in twitter usernames.
|
* Implemented a new setting, available in the account settings dialog, that allows to hide emojis in twitter usernames.
|
||||||
* Fixed error when attempting to mention an user by using the "mention" button in any people buffer. Now tweets should be posted normally.
|
* Fixed error when attempting to mention an user by using the "mention" button in any people buffer. Now tweets should be posted normally.
|
||||||
* Fixed error when loading other user lists. ([#465](https://github.com/MCV-Software/TWBlue/issues/465))
|
* Fixed error when loading other user lists. ([#465](https://github.com/MCV-Software/TWBlue/issues/465))
|
||||||
|
2
src/controller/buffers/mastodon/__init__.py
Normal file
2
src/controller/buffers/mastodon/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from .base import BaseBuffer
|
141
src/controller/buffers/mastodon/base.py
Normal file
141
src/controller/buffers/mastodon/base.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" Common logic to all buffers in TWBlue."""
|
||||||
|
import logging
|
||||||
|
import wx
|
||||||
|
import output
|
||||||
|
import sound
|
||||||
|
import widgetUtils
|
||||||
|
|
||||||
|
log = logging.getLogger("controller.buffers.base.base")
|
||||||
|
|
||||||
|
class Buffer(object):
|
||||||
|
""" A basic buffer object. This should be the base class for all other derived buffers."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None, function=None, session=None, *args, **kwargs):
|
||||||
|
"""Inits the main controller for this buffer:
|
||||||
|
@ parent wx.Treebook object: Container where we will put this buffer.
|
||||||
|
@ function str or None: function to be called periodically and update items on this buffer.
|
||||||
|
@ session sessionmanager.session object or None: Session handler for settings, database and data access.
|
||||||
|
"""
|
||||||
|
super(Buffer, self).__init__()
|
||||||
|
self.function = function
|
||||||
|
# Compose_function will be used to render an object on this buffer. Normally, signature is as follows:
|
||||||
|
# compose_function(item, db, relative_times, show_screen_names=False, session=None)
|
||||||
|
# Read more about compose functions in sessions/twitter/compose.py.
|
||||||
|
self.compose_function = None
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
# This will be used as a reference to the wx.Panel object wich stores the buffer GUI.
|
||||||
|
self.buffer = None
|
||||||
|
# This should countains the account associated to this buffer.
|
||||||
|
self.account = ""
|
||||||
|
# This controls whether the start_stream function should be called when starting the program.
|
||||||
|
self.needs_init = True
|
||||||
|
# if this is set to False, the buffer will be ignored on the invisible interface.
|
||||||
|
self.invisible = False
|
||||||
|
# Control variable, used to track time of execution for calls to start_stream.
|
||||||
|
self.execution_time = 0
|
||||||
|
|
||||||
|
def clear_list(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_event(self, ev):
|
||||||
|
""" Catch key presses in the WX interface and generate the corresponding event names."""
|
||||||
|
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "audio"
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "url"
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_DELETE and ev.ShiftDown(): event = "clear_list"
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_DELETE: event = "destroy_status"
|
||||||
|
# Raise a Special event when pressed Shift+F10 because Wx==4.1.x does not seems to trigger this by itself.
|
||||||
|
# See https://github.com/manuelcortez/TWBlue/issues/353
|
||||||
|
elif ev.GetKeyCode() == wx.WXK_F10 and ev.ShiftDown(): event = "show_menu"
|
||||||
|
else:
|
||||||
|
event = None
|
||||||
|
ev.Skip()
|
||||||
|
if event != None:
|
||||||
|
try:
|
||||||
|
### ToDo: Remove after WX fixes issue #353 in the widgets.
|
||||||
|
if event == "show_menu":
|
||||||
|
return self.show_menu(widgetUtils.MENU, pos=self.buffer.list.list.GetPosition())
|
||||||
|
getattr(self, event)()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def volume_down(self):
|
||||||
|
""" Decreases volume by 5%"""
|
||||||
|
if self.session.settings["sound"]["volume"] > 0.0:
|
||||||
|
if self.session.settings["sound"]["volume"] <= 0.05:
|
||||||
|
self.session.settings["sound"]["volume"] = 0.0
|
||||||
|
else:
|
||||||
|
self.session.settings["sound"]["volume"] -=0.05
|
||||||
|
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100.0))
|
||||||
|
self.session.sound.play("volume_changed.ogg")
|
||||||
|
self.session.settings.write()
|
||||||
|
|
||||||
|
def volume_up(self):
|
||||||
|
""" Increases volume by 5%."""
|
||||||
|
if self.session.settings["sound"]["volume"] < 1.0:
|
||||||
|
if self.session.settings["sound"]["volume"] >= 0.95:
|
||||||
|
self.session.settings["sound"]["volume"] = 1.0
|
||||||
|
else:
|
||||||
|
self.session.settings["sound"]["volume"] +=0.05
|
||||||
|
sound.URLPlayer.player.audio_set_volume(int(self.session.settings["sound"]["volume"]*100))
|
||||||
|
self.session.sound.play("volume_changed.ogg")
|
||||||
|
self.session.settings.write()
|
||||||
|
|
||||||
|
def start_stream(self, mandatory=False, play_sound=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_more_items(self):
|
||||||
|
output.speak(_(u"This action is not supported for this buffer"), True)
|
||||||
|
|
||||||
|
def put_items_on_list(self, items):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def remove_buffer(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def remove_item(self, item):
|
||||||
|
f = self.buffer.list.get_selected()
|
||||||
|
self.buffer.list.remove_item(item)
|
||||||
|
self.buffer.list.select_item(f)
|
||||||
|
|
||||||
|
def bind_events(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return self.buffer
|
||||||
|
|
||||||
|
def get_message(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_list_position(self, reversed=False):
|
||||||
|
if reversed == False:
|
||||||
|
self.buffer.list.select_item(-1)
|
||||||
|
else:
|
||||||
|
self.buffer.list.select_item(0)
|
||||||
|
|
||||||
|
def reply(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def send_message(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def share_item(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def can_share(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def destroy_status(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def post_status(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def save_positions(self):
|
||||||
|
try:
|
||||||
|
self.session.db[self.name+"_pos"]=self.buffer.list.get_selected()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
@ -18,6 +18,7 @@ if system == "Windows":
|
|||||||
from . import user
|
from . import user
|
||||||
from . import listsController
|
from . import listsController
|
||||||
from . import filterController
|
from . import filterController
|
||||||
|
from . import userSelector
|
||||||
elif system == "Linux":
|
elif system == "Linux":
|
||||||
from gtkUI import (view, commonMessageDialogs)
|
from gtkUI import (view, commonMessageDialogs)
|
||||||
from sessions.twitter import utils, compose
|
from sessions.twitter import utils, compose
|
||||||
@ -518,10 +519,9 @@ class Controller(object):
|
|||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
selector = userSelector.userSelector(users=users, session_id=buff.session.session_id)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
user = selector.get_user()
|
||||||
user = dlg.get_user()
|
if user == None:
|
||||||
else:
|
|
||||||
return
|
return
|
||||||
l = listsController.listsController(buff.session, user=user)
|
l = listsController.listsController(buff.session, user=user)
|
||||||
|
|
||||||
@ -535,10 +535,9 @@ class Controller(object):
|
|||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
selector = userSelector.userSelector(users=users, session_id=buff.session.session_id)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
user = selector.get_user()
|
||||||
user = dlg.get_user()
|
if user == None:
|
||||||
else:
|
|
||||||
return
|
return
|
||||||
dlg = dialogs.lists.addUserListDialog()
|
dlg = dialogs.lists.addUserListDialog()
|
||||||
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
|
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
|
||||||
@ -564,10 +563,9 @@ class Controller(object):
|
|||||||
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
users = [buff.session.get_user(tweet.message_create["sender_id"]).screen_name]
|
||||||
else:
|
else:
|
||||||
users = utils.get_all_users(tweet, buff.session)
|
users = utils.get_all_users(tweet, buff.session)
|
||||||
dlg = dialogs.utils.selectUserDialog(_(u"Select the user"), users)
|
selector = userSelector.userSelector(users=users, session_id=buff.session.session_id)
|
||||||
if dlg.get_response() == widgetUtils.OK:
|
user = selector.get_user()
|
||||||
user = dlg.get_user()
|
if user == None:
|
||||||
else:
|
|
||||||
return
|
return
|
||||||
dlg = dialogs.lists.removeUserListDialog()
|
dlg = dialogs.lists.removeUserListDialog()
|
||||||
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
|
dlg.populate_list([compose.compose_list(item) for item in buff.session.db["lists"]])
|
||||||
|
@ -11,8 +11,9 @@ from pubsub import pub
|
|||||||
from twitter_text import parse_tweet
|
from twitter_text import parse_tweet
|
||||||
from wxUI.dialogs import twitterDialogs, urlList
|
from wxUI.dialogs import twitterDialogs, urlList
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from extra import translator, SpellChecker, autocompletionUsers
|
from extra import translator, SpellChecker
|
||||||
from extra.AudioUploader import audioUploader
|
from extra.AudioUploader import audioUploader
|
||||||
|
from extra.autocompletionUsers import completion
|
||||||
from sessions.twitter import utils
|
from sessions.twitter import utils
|
||||||
|
|
||||||
class basicTweet(object):
|
class basicTweet(object):
|
||||||
@ -160,7 +161,7 @@ class tweet(basicTweet):
|
|||||||
self.text_processor()
|
self.text_processor()
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
def autocomplete_users(self, *args, **kwargs):
|
||||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
c = completion.autocompletionUsers(self.message, self.session.session_id)
|
||||||
c.show_menu()
|
c.show_menu()
|
||||||
|
|
||||||
def add_tweet(self, event, update_gui=True, *args, **kwargs):
|
def add_tweet(self, event, update_gui=True, *args, **kwargs):
|
||||||
@ -269,7 +270,7 @@ class dm(basicTweet):
|
|||||||
self.text_processor()
|
self.text_processor()
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
def autocomplete_users(self, *args, **kwargs):
|
||||||
c = autocompletionUsers.completion.autocompletionUsers(self.message, self.session.session_id)
|
c = completion.autocompletionUsers(self.message, self.session.session_id)
|
||||||
c.show_menu("dm")
|
c.show_menu("dm")
|
||||||
|
|
||||||
def text_processor(self, *args, **kwargs):
|
def text_processor(self, *args, **kwargs):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
import threading
|
||||||
import logging
|
import logging
|
||||||
import sound_lib
|
import sound_lib
|
||||||
import paths
|
import paths
|
||||||
@ -16,7 +17,7 @@ from pubsub import pub
|
|||||||
from mysc import autostart as autostart_windows
|
from mysc import autostart as autostart_windows
|
||||||
from wxUI.dialogs import configuration
|
from wxUI.dialogs import configuration
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
from extra.autocompletionUsers import settings
|
from extra.autocompletionUsers import scan, manage
|
||||||
from extra.ocr import OCRSpace
|
from extra.ocr import OCRSpace
|
||||||
from .editTemplateController import EditTemplate
|
from .editTemplateController import EditTemplate
|
||||||
|
|
||||||
@ -142,7 +143,8 @@ class accountSettingsController(globalSettingsController):
|
|||||||
|
|
||||||
def create_config(self):
|
def create_config(self):
|
||||||
self.dialog.create_general_account()
|
self.dialog.create_general_account()
|
||||||
widgetUtils.connect_event(self.dialog.general.au, widgetUtils.BUTTON_PRESSED, self.manage_autocomplete)
|
widgetUtils.connect_event(self.dialog.general.userAutocompletionScan, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_scan)
|
||||||
|
widgetUtils.connect_event(self.dialog.general.userAutocompletionManage, widgetUtils.BUTTON_PRESSED, self.on_autocompletion_manage)
|
||||||
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
|
self.dialog.set_value("general", "relative_time", self.config["general"]["relative_times"])
|
||||||
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
|
self.dialog.set_value("general", "show_screen_names", self.config["general"]["show_screen_names"])
|
||||||
self.dialog.set_value("general", "hide_emojis", self.config["general"]["hide_emojis"])
|
self.dialog.set_value("general", "hide_emojis", self.config["general"]["hide_emojis"])
|
||||||
@ -304,8 +306,19 @@ class accountSettingsController(globalSettingsController):
|
|||||||
def toggle_state(self,*args,**kwargs):
|
def toggle_state(self,*args,**kwargs):
|
||||||
return self.dialog.buffers.change_selected_item()
|
return self.dialog.buffers.change_selected_item()
|
||||||
|
|
||||||
def manage_autocomplete(self, *args, **kwargs):
|
def on_autocompletion_scan(self, *args, **kwargs):
|
||||||
configuration = settings.autocompletionSettings(self.buffer.session.settings, self.buffer, self.window)
|
configuration = scan.autocompletionScan(self.buffer.session.settings, self.buffer, self.window)
|
||||||
|
to_scan = configuration.show_dialog()
|
||||||
|
if to_scan == True:
|
||||||
|
configuration.prepare_progress_dialog()
|
||||||
|
t = threading.Thread(target=configuration.scan)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_autocompletion_manage(self, *args, **kwargs):
|
||||||
|
configuration = manage.autocompletionManage(self.buffer.session)
|
||||||
|
configuration.show_settings()
|
||||||
|
|
||||||
def add_ignored_client(self, *args, **kwargs):
|
def add_ignored_client(self, *args, **kwargs):
|
||||||
client = commonMessageDialogs.get_ignored_client()
|
client = commonMessageDialogs.get_ignored_client()
|
||||||
|
@ -4,7 +4,7 @@ import output
|
|||||||
from wxUI.dialogs import userActions
|
from wxUI.dialogs import userActions
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from extra import autocompletionUsers
|
from extra.autocompletionUsers import completion
|
||||||
|
|
||||||
class userActionsController(object):
|
class userActionsController(object):
|
||||||
def __init__(self, buffer, users=[], default="follow"):
|
def __init__(self, buffer, users=[], default="follow"):
|
||||||
@ -17,7 +17,7 @@ class userActionsController(object):
|
|||||||
self.process_action()
|
self.process_action()
|
||||||
|
|
||||||
def autocomplete_users(self, *args, **kwargs):
|
def autocomplete_users(self, *args, **kwargs):
|
||||||
c = autocompletionUsers.completion.autocompletionUsers(self.dialog, self.session.session_id)
|
c = completion.autocompletionUsers(self.dialog, self.session.session_id)
|
||||||
c.show_menu("dm")
|
c.show_menu("dm")
|
||||||
|
|
||||||
def process_action(self):
|
def process_action(self):
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
import widgetUtils
|
import widgetUtils
|
||||||
from pubsub import pub
|
from pubsub import pub
|
||||||
from wxUI.dialogs import userAliasDialogs
|
from wxUI.dialogs import userAliasDialogs
|
||||||
from extra import autocompletionUsers
|
|
||||||
|
|
||||||
class userAliasController(object):
|
class userAliasController(object):
|
||||||
def __init__(self, settings):
|
def __init__(self, settings):
|
||||||
|
39
src/controller/userSelector.py
Normal file
39
src/controller/userSelector.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" Small utility dessigned to select users from the currently focused item or the autocomplete database. """
|
||||||
|
import wx
|
||||||
|
import widgetUtils
|
||||||
|
from wxUI.dialogs import utils
|
||||||
|
from extra.autocompletionUsers import completion
|
||||||
|
|
||||||
|
class userSelector(object):
|
||||||
|
|
||||||
|
def __init__(self, users, session_id, title=_("Select user")):
|
||||||
|
""" Creates a dialog that chooses an user selector, from where users who have the autocomplete database already filled can also use that feature.
|
||||||
|
|
||||||
|
:param users: lists of users extracted from the currently focused item.
|
||||||
|
:type users: list
|
||||||
|
:param session_id: ID of the session to instantiate autocomplete database.
|
||||||
|
:type session_id: str
|
||||||
|
:param title: Title of the user selector dialog.
|
||||||
|
:type title: str
|
||||||
|
"""
|
||||||
|
self.session_id = session_id
|
||||||
|
self.dlg = utils.selectUserDialog(users=users, title=title)
|
||||||
|
widgetUtils.connect_event(self.dlg.autocompletion, widgetUtils.BUTTON_PRESSED, self.on_autocomplete_users)
|
||||||
|
|
||||||
|
def on_autocomplete_users(self, *args, **kwargs):
|
||||||
|
""" performs user autocompletion, if configured properly. """
|
||||||
|
c = completion.autocompletionUsers(self.dlg, self.session_id)
|
||||||
|
c.show_menu("dm")
|
||||||
|
|
||||||
|
def get_user(self):
|
||||||
|
""" Actually shows the dialog and returns an user if the dialog was accepted, None otherwise.
|
||||||
|
|
||||||
|
:rtype: str or None
|
||||||
|
"""
|
||||||
|
if self.dlg.ShowModal() == wx.ID_OK:
|
||||||
|
user = self.dlg.get_user()
|
||||||
|
else:
|
||||||
|
user = None
|
||||||
|
self.dlg.Destroy()
|
||||||
|
return user
|
@ -1,4 +1,2 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# -*- coding: utf-8 -*-
|
""" Autocompletion users for TWBlue. This package contains all needed code to support this feature, including automatic addition of users, management and code to show the autocompletion menu when an user is composing a tweet. """
|
||||||
""" Autocompletion users feature for TWBlue. This package contains all needed code to support this feature, including automatic addition of users, management and code to show the autocompletion menu when an user is composing a tweet. """
|
|
||||||
from . import completion, settings
|
|
||||||
|
@ -8,7 +8,7 @@ class autocompletionUsers(object):
|
|||||||
def __init__(self, window, session_id):
|
def __init__(self, window, session_id):
|
||||||
""" Class constructor. Displays a menu with users matching the specified pattern for autocompletion.
|
""" Class constructor. Displays a menu with users matching the specified pattern for autocompletion.
|
||||||
|
|
||||||
:param window: A wx control where the menu should be displayed.
|
:param window: A wx control where the menu should be displayed. Normally this is going to be the wx.TextCtrl indicating the tweet's text or direct message recipient.
|
||||||
:type window: wx.Dialog
|
:type window: wx.Dialog
|
||||||
:param session_id: Session ID which calls this class. We will load the users database from this session.
|
:param session_id: Session ID which calls this class. We will load the users database from this session.
|
||||||
:type session_id: str.
|
:type session_id: str.
|
||||||
@ -29,8 +29,8 @@ class autocompletionUsers(object):
|
|||||||
:param mode: this controls how the dialog will behave. Possible values are 'tweet' and 'dm'. In tweet mode, the matching pattern will be @user (@ is required), while in 'dm' mode the matching pattern will be anything written in the text control.
|
:param mode: this controls how the dialog will behave. Possible values are 'tweet' and 'dm'. In tweet mode, the matching pattern will be @user (@ is required), while in 'dm' mode the matching pattern will be anything written in the text control.
|
||||||
:type mode: str
|
:type mode: str
|
||||||
"""
|
"""
|
||||||
position = self.window.text.GetInsertionPoint()
|
|
||||||
if mode == "tweet":
|
if mode == "tweet":
|
||||||
|
position = self.window.text.GetInsertionPoint()
|
||||||
text = self.window.text.GetValue()
|
text = self.window.text.GetValue()
|
||||||
text = text[:position]
|
text = text[:position]
|
||||||
try:
|
try:
|
||||||
@ -60,7 +60,7 @@ class autocompletionUsers(object):
|
|||||||
users = self.db.get_users(pattern)
|
users = self.db.get_users(pattern)
|
||||||
if len(users) > 0:
|
if len(users) > 0:
|
||||||
menu.append_options(users)
|
menu.append_options(users)
|
||||||
self.window.PopupMenu(menu, self.window.text.GetPosition())
|
self.window.PopupMenu(menu, self.window.cb.GetPosition())
|
||||||
menu.destroy()
|
menu.destroy()
|
||||||
else:
|
else:
|
||||||
output.speak(_(u"There are no results in your users database"))
|
output.speak(_(u"There are no results in your users database"))
|
||||||
|
@ -1,15 +1,27 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
""" Management of users in the local database for autocompletion. """
|
||||||
|
import time
|
||||||
import widgetUtils
|
import widgetUtils
|
||||||
|
from tweepy.cursor import Cursor
|
||||||
from tweepy.errors import TweepyException
|
from tweepy.errors import TweepyException
|
||||||
from . import storage, wx_manage
|
|
||||||
from wxUI import commonMessageDialogs
|
from wxUI import commonMessageDialogs
|
||||||
|
from . import storage, wx_manage
|
||||||
|
|
||||||
class autocompletionManage(object):
|
class autocompletionManage(object):
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
|
""" class constructor. Manages everything related to user autocompletion.
|
||||||
|
|
||||||
|
:param session: Sessiom where the autocompletion management has been requested.
|
||||||
|
:type session: sessions.base.Session.
|
||||||
|
"""
|
||||||
super(autocompletionManage, self).__init__()
|
super(autocompletionManage, self).__init__()
|
||||||
self.session = session
|
self.session = session
|
||||||
self.dialog = wx_manage.autocompletionManageDialog()
|
# Instantiate database so we can perform modifications on it.
|
||||||
self.database = storage.storage(self.session.session_id)
|
self.database = storage.storage(self.session.session_id)
|
||||||
|
|
||||||
|
def show_settings(self):
|
||||||
|
""" display user management dialog and connect events associated to it. """
|
||||||
|
self.dialog = wx_manage.autocompletionManageDialog()
|
||||||
self.users = self.database.get_all_users()
|
self.users = self.database.get_all_users()
|
||||||
self.dialog.put_users(self.users)
|
self.dialog.put_users(self.users)
|
||||||
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
|
widgetUtils.connect_event(self.dialog.add, widgetUtils.BUTTON_PRESSED, self.add_user)
|
||||||
@ -17,6 +29,7 @@ class autocompletionManage(object):
|
|||||||
self.dialog.get_response()
|
self.dialog.get_response()
|
||||||
|
|
||||||
def update_list(self):
|
def update_list(self):
|
||||||
|
""" update users list in management dialog. This function is normallhy used after we modify the database in any way, so we can reload all users in the autocompletion user management list. """
|
||||||
item = self.dialog.users.get_selected()
|
item = self.dialog.users.get_selected()
|
||||||
self.dialog.users.clear()
|
self.dialog.users.clear()
|
||||||
self.users = self.database.get_all_users()
|
self.users = self.database.get_all_users()
|
||||||
@ -24,9 +37,12 @@ class autocompletionManage(object):
|
|||||||
self.dialog.users.select_item(item)
|
self.dialog.users.select_item(item)
|
||||||
|
|
||||||
def add_user(self, *args, **kwargs):
|
def add_user(self, *args, **kwargs):
|
||||||
|
""" Add a new Twitter username to the autocompletion database. """
|
||||||
usr = self.dialog.get_user()
|
usr = self.dialog.get_user()
|
||||||
if usr == False:
|
if usr == False:
|
||||||
return
|
return
|
||||||
|
# check if user exists.
|
||||||
|
# ToDo: in case we want to adapt this for other networks we'd need to refactor this check.
|
||||||
try:
|
try:
|
||||||
data = self.session.twitter.get_user(screen_name=usr)
|
data = self.session.twitter.get_user(screen_name=usr)
|
||||||
except TweepyException as e:
|
except TweepyException as e:
|
||||||
@ -36,7 +52,8 @@ class autocompletionManage(object):
|
|||||||
self.database.set_user(data.screen_name, data.name, 0)
|
self.database.set_user(data.screen_name, data.name, 0)
|
||||||
self.update_list()
|
self.update_list()
|
||||||
|
|
||||||
def remove_user(self, ev):
|
def remove_user(self, *args, **kwargs):
|
||||||
|
""" Remove focused user from the autocompletion database. """
|
||||||
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
|
if commonMessageDialogs.delete_user_from_db() == widgetUtils.YES:
|
||||||
item = self.dialog.users.get_selected()
|
item = self.dialog.users.get_selected()
|
||||||
user = self.users[item]
|
user = self.users[item]
|
||||||
|
110
src/extra/autocompletionUsers/scan.py
Normal file
110
src/extra/autocompletionUsers/scan.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" Scanning code for autocompletion feature on TWBlue. This module can retrieve user objects from the selected Twitter account automatically. """
|
||||||
|
import time
|
||||||
|
import wx
|
||||||
|
import widgetUtils
|
||||||
|
import output
|
||||||
|
from tweepy.cursor import Cursor
|
||||||
|
from tweepy.errors import TweepyException
|
||||||
|
from pubsub import pub
|
||||||
|
from . import wx_scan
|
||||||
|
from . import manage
|
||||||
|
from . import storage
|
||||||
|
|
||||||
|
class autocompletionScan(object):
|
||||||
|
def __init__(self, config, buffer, window):
|
||||||
|
""" Class constructor. This class will take care of scanning the selected Twitter account to populate the database with users automatically upon request.
|
||||||
|
|
||||||
|
:param config: Config for the session that will be scanned in search for users.
|
||||||
|
:type config: dict
|
||||||
|
:param buffer: home buffer for the focused session.
|
||||||
|
:type buffer: controller.buffers.twitter.base.baseBuffer
|
||||||
|
:param window: Main Window of TWBlue.
|
||||||
|
:type window:wx.Frame
|
||||||
|
"""
|
||||||
|
super(autocompletionScan, self).__init__()
|
||||||
|
self.config = config
|
||||||
|
self.buffer = buffer
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
def show_dialog(self):
|
||||||
|
self.dialog = wx_scan.autocompletionScanDialog()
|
||||||
|
self.dialog.set("friends", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
||||||
|
self.dialog.set("followers", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
||||||
|
if self.dialog.get_response() == widgetUtils.OK:
|
||||||
|
confirmation = wx_scan.confirm()
|
||||||
|
return confirmation
|
||||||
|
|
||||||
|
def prepare_progress_dialog(self):
|
||||||
|
self.progress_dialog = wx_scan.autocompletionScanProgressDialog()
|
||||||
|
# connect method to update progress dialog
|
||||||
|
pub.subscribe(self.on_update_progress, "on-update-progress")
|
||||||
|
self.progress_dialog.Show()
|
||||||
|
|
||||||
|
def on_update_progress(self, percent):
|
||||||
|
if percent > 100:
|
||||||
|
percent = 100
|
||||||
|
wx.CallAfter(self.progress_dialog.update, percent)
|
||||||
|
|
||||||
|
def scan(self):
|
||||||
|
""" Attempts to add all users selected by current user to the autocomplete database. """
|
||||||
|
ids = []
|
||||||
|
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends")
|
||||||
|
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers")
|
||||||
|
output.speak(_("Updating database... You can close this window now. A message will tell you when the process finishes."))
|
||||||
|
database = storage.storage(self.buffer.session.session_id)
|
||||||
|
percent = 0
|
||||||
|
# Retrieve ids of all following users
|
||||||
|
if self.dialog.get("friends") == True:
|
||||||
|
for i in Cursor(self.buffer.session.twitter.get_friend_ids, count=5000).items():
|
||||||
|
if str(i) not in ids:
|
||||||
|
ids.append(str(i))
|
||||||
|
# same step, but for followers.
|
||||||
|
if self.dialog.get("followers") == True:
|
||||||
|
try:
|
||||||
|
for i in Cursor(self.buffer.session.twitter.get_follower_ids, count=5000).items():
|
||||||
|
if str(i) not in ids:
|
||||||
|
ids.append(str(i))
|
||||||
|
except TweepyException:
|
||||||
|
wx.CallAfter(wx_scan.show_error)
|
||||||
|
return self.done()
|
||||||
|
# As next step requires batches of 100s users, let's split our user Ids so we won't break the param rules.
|
||||||
|
split_users = [ids[i:i + 100] for i in range(0, len(ids), 100)]
|
||||||
|
# store returned users in this list.
|
||||||
|
users = []
|
||||||
|
for z in split_users:
|
||||||
|
if len(z) == 0:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
results = self.buffer.session.twitter.lookup_users(user_id=z)
|
||||||
|
except TweepyException:
|
||||||
|
wx.CallAfter(wx_scan.show_error)
|
||||||
|
return self.done()
|
||||||
|
users.extend(results)
|
||||||
|
time.sleep(1)
|
||||||
|
percent = percent + (100/len(split_users))
|
||||||
|
pub.sendMessage("on-update-progress", percent=percent)
|
||||||
|
for user in users:
|
||||||
|
database.set_user(user.screen_name, user.name, 1)
|
||||||
|
wx.CallAfter(wx_scan.show_success, len(users))
|
||||||
|
self.done()
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
wx.CallAfter(self.progress_dialog.Destroy)
|
||||||
|
wx.CallAfter(self.dialog.Destroy)
|
||||||
|
pub.unsubscribe(self.on_update_progress, "on-update-progress")
|
||||||
|
|
||||||
|
def execute_at_startup(window, buffer, config):
|
||||||
|
database = storage.storage(buffer.session.session_id)
|
||||||
|
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
||||||
|
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
||||||
|
for i in buffer.session.db[buffer.name]:
|
||||||
|
database.set_user(i.screen_name, i.name, 1)
|
||||||
|
else:
|
||||||
|
database.remove_by_buffer(1)
|
||||||
|
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
||||||
|
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
||||||
|
for i in buffer.session.db[buffer.name]:
|
||||||
|
database.set_user(i.screen_name, i.name, 2)
|
||||||
|
else:
|
||||||
|
database.remove_by_buffer(2)
|
@ -1,59 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import widgetUtils
|
|
||||||
import output
|
|
||||||
from . import wx_settings
|
|
||||||
from . import manage
|
|
||||||
from . import storage
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
|
|
||||||
class autocompletionSettings(object):
|
|
||||||
def __init__(self, config, buffer, window):
|
|
||||||
super(autocompletionSettings, self).__init__()
|
|
||||||
self.config = config
|
|
||||||
self.buffer = buffer
|
|
||||||
self.window = window
|
|
||||||
self.dialog = wx_settings.autocompletionSettingsDialog()
|
|
||||||
self.dialog.set("friends_buffer", self.config["mysc"]["save_friends_in_autocompletion_db"])
|
|
||||||
self.dialog.set("followers_buffer", self.config["mysc"]["save_followers_in_autocompletion_db"])
|
|
||||||
widgetUtils.connect_event(self.dialog.viewList, widgetUtils.BUTTON_PRESSED, self.view_list)
|
|
||||||
if self.dialog.get_response() == widgetUtils.OK:
|
|
||||||
call_threaded(self.add_users_to_database)
|
|
||||||
|
|
||||||
def add_users_to_database(self):
|
|
||||||
self.config["mysc"]["save_friends_in_autocompletion_db"] = self.dialog.get("friends_buffer")
|
|
||||||
self.config["mysc"]["save_followers_in_autocompletion_db"] = self.dialog.get("followers_buffer")
|
|
||||||
output.speak(_(u"Updating database... You can close this window now. A message will tell you when the process finishes."))
|
|
||||||
database = storage.storage(self.buffer.session.session_id)
|
|
||||||
if self.dialog.get("followers_buffer") == True:
|
|
||||||
buffer = self.window.search_buffer("followers", self.config["twitter"]["user_name"])
|
|
||||||
for i in buffer.session.db[buffer.name]:
|
|
||||||
database.set_user(i.screen_name, i.name, 1)
|
|
||||||
else:
|
|
||||||
database.remove_by_buffer(1)
|
|
||||||
if self.dialog.get("friends_buffer") == True:
|
|
||||||
buffer = self.window.search_buffer("friends", self.config["twitter"]["user_name"])
|
|
||||||
for i in buffer.session.db[buffer.name]:
|
|
||||||
database.set_user(i.screen_name, i.name, 2)
|
|
||||||
else:
|
|
||||||
database.remove_by_buffer(2)
|
|
||||||
wx_settings.show_success_dialog()
|
|
||||||
self.dialog.destroy()
|
|
||||||
|
|
||||||
def view_list(self, ev):
|
|
||||||
q = manage.autocompletionManage(self.buffer.session)
|
|
||||||
|
|
||||||
|
|
||||||
def execute_at_startup(window, buffer, config):
|
|
||||||
database = storage.storage(buffer.session.session_id)
|
|
||||||
if config["mysc"]["save_followers_in_autocompletion_db"] == True and config["other_buffers"]["show_followers"] == True:
|
|
||||||
buffer = window.search_buffer("followers", config["twitter"]["user_name"])
|
|
||||||
for i in buffer.session.db[buffer.name]:
|
|
||||||
database.set_user(i.screen_name, i.name, 1)
|
|
||||||
else:
|
|
||||||
database.remove_by_buffer(1)
|
|
||||||
if config["mysc"]["save_friends_in_autocompletion_db"] == True and config["other_buffers"]["show_friends"] == True:
|
|
||||||
buffer = window.search_buffer("friends", config["twitter"]["user_name"])
|
|
||||||
for i in buffer.session.db[buffer.name]:
|
|
||||||
database.set_user(i.screen_name, i.name, 2)
|
|
||||||
else:
|
|
||||||
database.remove_by_buffer(2)
|
|
@ -11,7 +11,7 @@ class menu(wx.Menu):
|
|||||||
def append_options(self, options):
|
def append_options(self, options):
|
||||||
for i in options:
|
for i in options:
|
||||||
item = wx.MenuItem(self, wx.ID_ANY, "%s (@%s)" % (i[1], i[0]))
|
item = wx.MenuItem(self, wx.ID_ANY, "%s (@%s)" % (i[1], i[0]))
|
||||||
self.AppendItem(item)
|
self.Append(item)
|
||||||
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
|
self.Bind(wx.EVT_MENU, lambda evt, temp=i[0]: self.select_text(evt, temp), item)
|
||||||
|
|
||||||
def select_text(self, ev, text):
|
def select_text(self, ev, text):
|
||||||
|
48
src/extra/autocompletionUsers/wx_scan.py
Normal file
48
src/extra/autocompletionUsers/wx_scan.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
import widgetUtils
|
||||||
|
import application
|
||||||
|
|
||||||
|
class autocompletionScanDialog(widgetUtils.BaseDialog):
|
||||||
|
def __init__(self):
|
||||||
|
super(autocompletionScanDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.followers = wx.CheckBox(panel, -1, _("Add followers to database"))
|
||||||
|
self.friends = wx.CheckBox(panel, -1, _("Add friends to database"))
|
||||||
|
sizer.Add(self.followers, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(self.friends, 0, wx.ALL, 5)
|
||||||
|
ok = wx.Button(panel, wx.ID_OK)
|
||||||
|
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||||
|
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(cancel, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
||||||
|
panel.SetSizer(sizer)
|
||||||
|
self.SetClientSize(sizer.CalcMin())
|
||||||
|
|
||||||
|
class autocompletionScanProgressDialog(widgetUtils.BaseDialog):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(autocompletionScanProgressDialog, self).__init__(parent=None, id=wx.ID_ANY, title=_("Updating autocompletion database"), *args, **kwargs)
|
||||||
|
panel = wx.Panel(self)
|
||||||
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.progress_bar = wx.Gauge(parent=panel)
|
||||||
|
sizer.Add(self.progress_bar)
|
||||||
|
panel.SetSizerAndFit(sizer)
|
||||||
|
|
||||||
|
def update(self, percent):
|
||||||
|
self.progress_bar.SetValue(percent)
|
||||||
|
|
||||||
|
def confirm():
|
||||||
|
with wx.MessageDialog(None, _("This process will retrieve the users you selected from Twitter, and add them to the user autocomplete database. Please note that if there are many users or you have tried to perform this action less than 15 minutes ago, TWBlue may reach a limit in Twitter API calls when trying to load the users into the database. If this happens, we will show you an error, in which case you will have to try this process again in a few minutes. If this process ends with no error, you will be redirected back to the account settings dialog. Do you want to continue?"), _("Attention"), style=wx.ICON_QUESTION|wx.YES_NO) as result:
|
||||||
|
if result.ShowModal() == wx.ID_YES:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def show_success(users):
|
||||||
|
with wx.MessageDialog(None, _("TWBlue has imported {} users successfully.").format(users), _("Done")) as dlg:
|
||||||
|
dlg.ShowModal()
|
||||||
|
|
||||||
|
def show_error():
|
||||||
|
with wx.MessageDialog(None, _("Error adding users from Twitter. Please try again in about 15 minutes."), _("Error"), style=wx.ICON_ERROR) as dlg:
|
||||||
|
dlg.ShowModal()
|
@ -1,27 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import wx
|
|
||||||
import widgetUtils
|
|
||||||
import application
|
|
||||||
|
|
||||||
class autocompletionSettingsDialog(widgetUtils.BaseDialog):
|
|
||||||
def __init__(self):
|
|
||||||
super(autocompletionSettingsDialog, self).__init__(parent=None, id=-1, title=_(u"Autocomplete users' settings"))
|
|
||||||
panel = wx.Panel(self)
|
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
||||||
self.followers_buffer = wx.CheckBox(panel, -1, _(u"Add users from followers buffer"))
|
|
||||||
self.friends_buffer = wx.CheckBox(panel, -1, _(u"Add users from friends buffer"))
|
|
||||||
sizer.Add(self.followers_buffer, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(self.friends_buffer, 0, wx.ALL, 5)
|
|
||||||
self.viewList = wx.Button(panel, -1, _(u"Manage database..."))
|
|
||||||
sizer.Add(self.viewList, 0, wx.ALL, 5)
|
|
||||||
ok = wx.Button(panel, wx.ID_OK)
|
|
||||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
|
||||||
sizerBtn = wx.BoxSizer(wx.HORIZONTAL)
|
|
||||||
sizerBtn.Add(ok, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(cancel, 0, wx.ALL, 5)
|
|
||||||
sizer.Add(sizerBtn, 0, wx.ALL, 5)
|
|
||||||
panel.SetSizer(sizer)
|
|
||||||
self.SetClientSize(sizer.CalcMin())
|
|
||||||
|
|
||||||
def show_success_dialog():
|
|
||||||
wx.MessageDialog(None, _(u"{0}'s database of users has been updated.").format(application.name,), _(u"Done"), wx.OK).ShowModal()
|
|
@ -99,8 +99,13 @@ class generalAccount(wx.Panel, baseDialog.BaseWXDialog):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(generalAccount, self).__init__(parent)
|
super(generalAccount, self).__init__(parent)
|
||||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||||
self.au = wx.Button(self, wx.ID_ANY, _(u"Autocompletion settings..."))
|
userAutocompletionBox = wx.StaticBox(self, label=_("User autocompletion settings"))
|
||||||
sizer.Add(self.au, 0, wx.ALL, 5)
|
self.userAutocompletionScan = wx.Button(self, wx.ID_ANY, _("Scan account and add friends and followers to the user autocompletion database"))
|
||||||
|
self.userAutocompletionManage = wx.Button(self, wx.ID_ANY, _("Manage autocompletion database"))
|
||||||
|
autocompletionSizer = wx.StaticBoxSizer(userAutocompletionBox, wx.HORIZONTAL)
|
||||||
|
autocompletionSizer.Add(self.userAutocompletionScan, 0, wx.ALL, 5)
|
||||||
|
autocompletionSizer.Add(self.userAutocompletionManage, 0, wx.ALL, 5)
|
||||||
|
sizer.Add(autocompletionSizer, 0, wx.ALL, 5)
|
||||||
self.relative_time = wx.CheckBox(self, wx.ID_ANY, _(U"Relative timestamps"))
|
self.relative_time = wx.CheckBox(self, wx.ID_ANY, _(U"Relative timestamps"))
|
||||||
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
sizer.Add(self.relative_time, 0, wx.ALL, 5)
|
||||||
tweetsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
|
tweetsPerCallBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
Loading…
Reference in New Issue
Block a user