Moved profile displayer to MVP pattern. User info is collected in a thread

This commit is contained in:
Manuel Cortez 2019-01-06 19:41:00 -06:00
parent 0f68d2d0fc
commit 73d2d4a95d
10 changed files with 160 additions and 158 deletions

View File

@ -27,7 +27,6 @@ from . import messages
from . import buffers from . import buffers
from . import player from . import player
from . import posts from . import posts
from . import profiles
from . import longpollthread from . import longpollthread
from . import selector from . import selector
@ -663,7 +662,7 @@ class Controller(object):
player.player.volume = 0 player.player.volume = 0
def user_profile(self, person): def user_profile(self, person):
p = profiles.userProfile(self.session, person) p = presenters.userProfilePresenter(session=self.session, user_id=person, view=views.userProfileDialog(), interactor=interactors.userProfileInteractor())
def view_my_profile(self, *args, **kwargs): def view_my_profile(self, *args, **kwargs):
self.user_profile(self.session.user_id) self.user_profile(self.session.user_id)

View File

@ -17,7 +17,7 @@ import webbrowser
import logging import logging
from sessionmanager import session, renderers, utils # We'll use some functions from there from sessionmanager import session, renderers, utils # We'll use some functions from there
from pubsub import pub from pubsub import pub
from wxUI.dialogs import postDialogs, urlList, profiles from wxUI.dialogs import postDialogs, urlList
from extra import SpellChecker, translator from extra import SpellChecker, translator
from mysc.thread_utils import call_threaded from mysc.thread_utils import call_threaded
from wxUI import menus from wxUI import menus
@ -510,107 +510,3 @@ class friendship(object):
def set_friends_list(self, friendslist): def set_friends_list(self, friendslist):
for i in friendslist: for i in friendslist:
self.dialog.friends.insert_item(False, *[i]) self.dialog.friends.insert_item(False, *[i])
class userProfile(object):
def __init__(self, session, user_id):
self.person = None
self.session = session
self.user_id = user_id
self.dialog = profiles.userProfile(title=_("Profile"))
self.dialog.create_controls("main_info")
self.dialog.realice()
self.get_basic_information()
if self.person != None:
self.dialog.get_response()
def get_basic_information(self):
""" Gets and inserts basic user information.
See https://vk.com/dev/users.get"""
fields = "first_name, last_name, bdate, city, country, home_town, photo_200_orig, online, site, status, last_seen, occupation, relation, relatives, personal, connections, activities, interests, music, movies, tv, books, games, about, quotes, can_write_private_message"
person = self.session.vk.client.users.get(user_ids=self.user_id, fields=fields)
if len(person) == 0:
return output.speak(_("Information for groups is not supported, yet."))
person = person[0]
print(person)
# Gets full name.
n = "{0} {1}".format(person["first_name"], person["last_name"])
# Gets birthdate.
if "bdate" in person and person["bdate"] != "":
self.dialog.main_info.enable("bdate")
if len(person["bdate"]) <= 5:
d = arrow.get(person["bdate"], "D.m")
self.dialog.main_info.set("bdate", d.format(_("MMMM D"), locale=languageHandler.curLang[:2]))
else:
d = arrow.get(person["bdate"], "D.M.YYYY")
self.dialog.main_info.set("bdate", d.format(_("MMMM D, YYYY"), locale=languageHandler.curLang[:2]))
# Gets current city and home town
city = ""
if "home_town" in person and person["home_town"] != "":
home_town = person["home_town"]
self.dialog.main_info.enable("home_town")
self.dialog.main_info.set("home_town", home_town)
if "city" in person and len(person["city"]) > 0:
city = person["city"]["title"]
if "country" in person and person["country"] != "":
if city != "":
city = city+", {0}".format(person["country"]["title"])
else:
city = person["country"]["title"]
self.dialog.main_info.enable("city")
self.dialog.main_info.set("city", city)
self.dialog.main_info.set("name", n)
self.dialog.SetTitle(_("{name}'s profile").format(name=n,))
# Gets website
if "site" in person and person["site"] != "":
self.dialog.main_info.enable("website")
self.dialog.main_info.set("website", person["site"])
self.dialog.main_info.enable("go_site")
widgetUtils.connect_event(self.dialog.main_info.go_site, widgetUtils.BUTTON_PRESSED, self.visit_website)
if "status" in person and person["status"] != "":
self.dialog.main_info.enable("status")
self.dialog.main_info.set("status", person["status"])
if "occupation" in person and person["occupation"] != None:
if person["occupation"]["type"] == "work": c1 = _("Work ")
elif person["occupation"]["type"] == "school": c1 = _("Student ")
elif person["occupation"]["type"] == "university": c1 = _("Student ")
if "name" in person["occupation"] and person["occupation"]["name"] != "":
c2 = _("In {0}").format(person["occupation"]["name"],)
else:
c2 = ""
self.dialog.main_info.enable("occupation")
self.dialog.main_info.set("occupation", c1+c2)
if "relation" in person and person["relation"] != 0:
print(person["relation"])
if person["relation"] == 1:
r = _("Single")
elif person["relation"] == 2:
if "relation_partner" in person:
r = _("Dating with {0} {1}").format(person["relation_partner"]["first_name"], person["relation_partner"]["last_name"])
else:
r = _("Dating")
elif person["relation"] == 3:
r = _("Engaged with {0} {1}").format(person["relation_partner"]["first_name"], person["relation_partner"]["last_name"])
elif person["relation"] == 4:
r = _("Married with {0} {1}").format(person["relation_partner"]["first_name"], person["relation_partner"]["last_name"])
elif person["relation"] == 5:
r = _("It's complicated")
elif person["relation"] == 6:
r = _("Actively searching")
elif person["relation"] == 7:
r = _("In love")
self.dialog.main_info.enable("relation")
self.dialog.main_info.relation.SetLabel(_("Relationship: ")+r)
if "last_seen" in person and person["last_seen"] != False:
original_date = arrow.get(person["last_seen"]["time"])
# Translators: This is the date of last seen
last_seen = _("{0}").format(original_date.humanize(locale=languageHandler.curLang[:2]),)
self.dialog.main_info.enable("last_seen")
self.dialog.main_info.set("last_seen", last_seen)
log.info("getting info...")
self.person = person
self.dialog.SetClientSize(self.dialog.sizer.CalcMin())
def visit_website(self, *args, **kwargs):
output.speak(_("Opening website..."))
webbrowser.open_new_tab(self.person["site"])

View File

@ -1,2 +1,3 @@
from . audioRecorder import * from . audioRecorder import *
from .configuration import * from .configuration import *
from .profiles import *

View File

@ -11,12 +11,17 @@ class baseInteractor(object):
pub.subscribe(self.enable_control, "{modulename}_enable_control".format(modulename=modulename)) pub.subscribe(self.enable_control, "{modulename}_enable_control".format(modulename=modulename))
pub.subscribe(self.set_label, "{modulename}_set_label".format(modulename=modulename)) pub.subscribe(self.set_label, "{modulename}_set_label".format(modulename=modulename))
pub.subscribe(self.focus_control, "{modulename}_focus_control".format(modulename=modulename)) pub.subscribe(self.focus_control, "{modulename}_focus_control".format(modulename=modulename))
pub.subscribe(self.set_title, "{modulename}_set_title".format(modulename=modulename))
def uninstall(self): def uninstall(self):
pub.unsubscribe(self.disable_control, "{modulename}_disable_control".format(modulename=self.modulename)) pub.unsubscribe(self.disable_control, "{modulename}_disable_control".format(modulename=self.modulename))
pub.unsubscribe(self.enable_control, "{modulename}_enable_control".format(modulename=self.modulename)) pub.unsubscribe(self.enable_control, "{modulename}_enable_control".format(modulename=self.modulename))
pub.unsubscribe(self.set_label, "{modulename}_set_label".format(modulename=self.modulename)) pub.unsubscribe(self.set_label, "{modulename}_set_label".format(modulename=self.modulename))
pub.unsubscribe(self.focus_control, "{modulename}_focus_control".format(modulename=self.modulename)) pub.unsubscribe(self.focus_control, "{modulename}_focus_control".format(modulename=self.modulename))
pub.unsubscribe(self.set_title, "{modulename}_set_title".format(modulename=self.modulename))
def start(self):
self.result = self.view.get_response()
def disable_control(self, control): def disable_control(self, control):
self.view.disable(control) self.view.disable(control)
@ -29,3 +34,6 @@ class baseInteractor(object):
def set_label(self, control, label): def set_label(self, control, label):
self.view.set(control, label) self.view.set(control, label)
def set_title(self, value):
self.view.SetTitle(value)

View File

@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import six
import wx
import widgetUtils
from pubsub import pub
from views.dialogs import urlList
from . import base
class userProfileInteractor(base.baseInteractor):
def enable_control(self, tab, control):
if not hasattr(self.view, tab):
raise AttributeError("The viw does not contain the specified tab.")
tab = getattr(self.view, tab)
if not hasattr(tab, control):
raise AttributeError("The control is not present in the tab.")
getattr(tab, control).Enable(True)
def set(self, tab, control, value):
if not hasattr(self.view, tab):
raise AttributeError("The viw does not contain the specified tab.")
tab = getattr(self.view, tab)
if not hasattr(tab, control):
raise AttributeError("The control is not present in the tab.")
control = getattr(tab, control)
control.SetValue(value)
def set_label(self, tab, control, value):
if not hasattr(self.view, tab):
raise AttributeError("The viw does not contain the specified tab.")
tab = getattr(self.view, tab)
if not hasattr(tab, control):
raise AttributeError("The control is not present in the tab.")
control = getattr(tab, control)
control.SetLabel(value)
def load_image(self, image):
image = wx.Image(stream=six.BytesIO(image.content))
try:
self.view.image.SetBitmap(wx.Bitmap(image))
except ValueError:
return
self.view.panel.Layout()
def install(self, *args, **kwargs):
super(userProfileInteractor, self).install(*args, **kwargs)
pub.subscribe(self.set, self.modulename+"_set")
pub.subscribe(self.load_image, self.modulename+"_load_image")
self.view.create_controls("main_info")
self.view.realice()
widgetUtils.connect_event(self.view.main_info.go_site, widgetUtils.BUTTON_PRESSED, self.on_visit_website)
def uninstall(self):
super(userProfileInteractor, self).uninstall()
pub.unsubscribe(self.set, self.modulename+"_set")
pub.unsubscribe(self.load_image, self.modulename+"_load_image")
def on_visit_website(self, *args, **kwargs):
urls = self.presenter.get_urls()
if len(urls) == 1:
self.presenter.visit_url(urls[0])
else:
dialog = urlList.urlList()
dialog.populate_list(urls)
if dialog.get_response() != widgetUtils.OK:
return
selected_url = urls[dialog.get_item()]
self.presenter.visit_url(selected_url)

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .audioRecorder import * from .audioRecorder import *
from .configuration import * from .configuration import *
from .profiles import *

View File

@ -1,16 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" A profile viewer and editor for VK user objects.""" """ A profile viewer and editor for VK user objects."""
from __future__ import unicode_literals from __future__ import unicode_literals
from future import standard_library
standard_library.install_aliases()
import six
import webbrowser import webbrowser
import logging import logging
import arrow import arrow
import requests import requests
import languageHandler import languageHandler
import output import output
from pubsub import pub from mysc.thread_utils import call_threaded
from sessionmanager import utils from sessionmanager import utils
from . import base from . import base
@ -24,14 +21,17 @@ class userProfilePresenter(base.basePresenter):
@user_id integer: User ID to retrieve information of. @user_id integer: User ID to retrieve information of.
At the current time, only users (and not communities) are supported. At the current time, only users (and not communities) are supported.
""" """
super(userProfilePresenter, self).__init__(view=view, interactor=interactor, modulename="user_profile")
# self.person will hold a reference to the user object when retrieved from VK. # self.person will hold a reference to the user object when retrieved from VK.
self.person = None self.person = None
self.session = session self.session = session
self.user_id = user_id self.user_id = user_id
self.get_basic_information() # Get information in a threaded way here.
if self.person != None: # Note: We do not handle any race condition here because due to the presenter only sending pubsub messages,
super(profilesPresenter, self).__init__(view=view, interactor=interactor, modulename="profiles") # Nothing happens if the pubsub send messages to the interactor after the latter has been destroyed.
# Pubsub messages are just skipped if there are no listeners for them.
call_threaded(self.get_basic_information)
self.run()
def get_basic_information(self): def get_basic_information(self):
""" Gets and inserts basic user information. """ Gets and inserts basic user information.
@ -46,26 +46,26 @@ class userProfilePresenter(base.basePresenter):
return output.speak(_("Information for groups is not supported, yet.")) return output.speak(_("Information for groups is not supported, yet."))
person = person[0] person = person[0]
# toDo: remove this print when I will be done with creation of profile viewer logic. # toDo: remove this print when I will be done with creation of profile viewer logic.
print(person) # print(person)
# From this part we will format data from VK so users will see it in the GUI control. # From this part we will format data from VK so users will see it in the GUI control.
# Format full name. # Format full name.
n = "{0} {1}".format(person["first_name"], person["last_name"]) n = "{0} {1}".format(person["first_name"], person["last_name"])
# Format birthdate. # Format birthdate.
if "bdate" in person and person["bdate"] != "": if "bdate" in person and person["bdate"] != "":
self.dialog.main_info.enable("bdate") self.send_message("enable_control", tab="main_info", control="bdate")
# VK can display dd.mm or dd.mm.yyyy birthdates. So let's compare the string lenght to handle both cases accordingly. # VK can display dd.mm or dd.mm.yyyy birthdates. So let's compare the string lenght to handle both cases accordingly.
if len(person["bdate"]) <= 5: # dd.mm if len(person["bdate"]) <= 5: # dd.mm
d = arrow.get(person["bdate"], "D.M") d = arrow.get(person["bdate"], "D.M")
self.dialog.main_info.set("bdate", d.format(_("MMMM D"), locale=languageHandler.curLang[:2])) self.send_message("set", tab="main_info", control="bdate", value=d.format(_("MMMM D"), locale=languageHandler.curLang[:2]))
else: # mm.dd.yyyy else: # mm.dd.yyyy
d = arrow.get(person["bdate"], "D.M.YYYY") d = arrow.get(person["bdate"], "D.M.YYYY")
self.dialog.main_info.set("bdate", d.format(_("MMMM D, YYYY"), locale=languageHandler.curLang[:2])) self.send_message("set", tab="main_info", control="bdate", value=d.format(_("MMMM D, YYYY"), locale=languageHandler.curLang[:2]))
# Format current city and home town # Format current city and home town
city = "" city = ""
if "home_town" in person and person["home_town"] != "": if "home_town" in person and person["home_town"] != "":
home_town = person["home_town"] home_town = person["home_town"]
self.dialog.main_info.enable("home_town") self.send_message("enable_control", tab="main_info", control="home_town")
self.dialog.main_info.set("home_town", home_town) self.send_message("set", tab="main_info", control="home_town", value=home_town)
if "city" in person and len(person["city"]) > 0: if "city" in person and len(person["city"]) > 0:
city = person["city"]["title"] city = person["city"]["title"]
if "country" in person and person["country"] != "": if "country" in person and person["country"] != "":
@ -73,20 +73,19 @@ class userProfilePresenter(base.basePresenter):
city = city+", {0}".format(person["country"]["title"]) city = city+", {0}".format(person["country"]["title"])
else: else:
city = person["country"]["title"] city = person["country"]["title"]
self.dialog.main_info.enable("city") self.send_message("enable_control", tab="main_info", control="city")
self.dialog.main_info.set("city", city) self.send_message("set", tab="main_info", control="city", value=city)
self.dialog.main_info.set("name", n) self.send_message("set", tab="main_info", control="name", value=n)
self.dialog.SetTitle(_("{name}'s profile").format(name=n,)) self.send_message("set_title", value=_("{name}'s profile").format(name=n,))
# Format website (or websites, if there are multiple of them). # Format website (or websites, if there are multiple of them).
if "site" in person and person["site"] != "": if "site" in person and person["site"] != "":
self.dialog.main_info.enable("website") self.send_message("enable_control", tab="main_info", control="website")
self.dialog.main_info.set("website", person["site"]) self.send_message("set", tab="main_info", control="website", value=person["site"])
self.dialog.main_info.enable("go_site") self.send_message("enable_control", tab="main_info", control="go_site")
widgetUtils.connect_event(self.dialog.main_info.go_site, widgetUtils.BUTTON_PRESSED, self.visit_website)
# Format status message. # Format status message.
if "status" in person and person["status"] != "": if "status" in person and person["status"] != "":
self.dialog.main_info.enable("status") self.send_message("enable_control", tab="main_info", control="status")
self.dialog.main_info.set("status", person["status"]) self.send_message("set", tab="main_info", control="status", value=person["status"])
# Format occupation. # Format occupation.
# toDo: Research in this field is needed. Sometimes it returns university information even if users have active work places. # toDo: Research in this field is needed. Sometimes it returns university information even if users have active work places.
if "occupation" in person and person["occupation"] != None: if "occupation" in person and person["occupation"] != None:
@ -97,8 +96,8 @@ class userProfilePresenter(base.basePresenter):
c2 = _("In {0}").format(person["occupation"]["name"],) c2 = _("In {0}").format(person["occupation"]["name"],)
else: else:
c2 = "" c2 = ""
self.dialog.main_info.enable("occupation") self.send_message("enable_control", tab="main_info", control="occupation")
self.dialog.main_info.set("occupation", c1+c2) self.send_message("set", tab="main_info", control="occupation", value=c1+c2)
# format relationship status. # format relationship status.
# ToDo: When dating someone, the button associated to the information should point to the profile of the user. # ToDo: When dating someone, the button associated to the information should point to the profile of the user.
if "relation" in person and person["relation"] != 0: if "relation" in person and person["relation"] != 0:
@ -119,28 +118,24 @@ class userProfilePresenter(base.basePresenter):
r = _("Actively searching") r = _("Actively searching")
elif person["relation"] == 7: elif person["relation"] == 7:
r = _("In love") r = _("In love")
self.dialog.main_info.enable("relation") self.send_message("enable_control", tab="main_info", control="relation")
self.dialog.main_info.relation.SetLabel(_("Relationship: ")+r) self.send_message("set_label", tab="main_info", control="relation", value=_("Relationship: ")+r)
# format last seen. # format last seen.
if "last_seen" in person and person["last_seen"] != False: if "last_seen" in person and person["last_seen"] != False:
original_date = arrow.get(person["last_seen"]["time"]) original_date = arrow.get(person["last_seen"]["time"])
# Translators: This is the date of last seen # Translators: This is the date of last seen
last_seen = _("{0}").format(original_date.humanize(locale=languageHandler.curLang[:2]),) last_seen = _("{0}").format(original_date.humanize(locale=languageHandler.curLang[:2]),)
self.dialog.main_info.enable("last_seen") self.send_message("enable_control", tab="main_info", control="last_seen")
self.dialog.main_info.set("last_seen", last_seen) self.send_message("set", tab="main_info", control="last_seen", value=last_seen)
self.person = person self.person = person
# Adds photo to the dialog. # Adds photo to the dialog.
# ToDo: Need to ask if this has a visible effect in the dialog. # ToDo: Need to ask if this has a visible effect in the dialog.
if "photo_200_orig" in person: if "photo_200_orig" in person:
img = requests.get(person["photo_200_orig"]) img = requests.get(person["photo_200_orig"])
image = wx.Image(stream=six.BytesIO(requests.get(person["photo_200_orig"]).content)) self.send_message("load_image", image=requests.get(person["photo_200_orig"]))
try: output.speak(_("Profile loaded"))
self.dialog.image.SetBitmap(wx.Bitmap(image))
except ValueError:
return
self.dialog.panel.Layout()
def visit_website(self, *args, **kwargs): def get_urls(self, *args, **kwargs):
""" Allows to visit an user's website. """ """ Allows to visit an user's website. """
text = self.person["site"] text = self.person["site"]
# Let's search for URLS with a regexp, as there are users with multiple websites in their profiles. # Let's search for URLS with a regexp, as there are users with multiple websites in their profiles.
@ -148,13 +143,8 @@ class userProfilePresenter(base.basePresenter):
if len(urls) == 0: if len(urls) == 0:
output.speak(_("No URL addresses were detected.")) output.speak(_("No URL addresses were detected."))
return return
elif len(urls) == 1: return urls
selected_url = urls[0]
else: def visit_url(self, url):
dialog = urlList.urlList()
dialog.populate_list(urls)
if dialog.get_response() != widgetUtils.OK:
return
selected_url = urls[dialog.get_item()]
output.speak(_("Opening URL...")) output.speak(_("Opening URL..."))
webbrowser.open_new_tab(selected_url) webbrowser.open_new_tab(url)

View File

@ -1,2 +1,3 @@
from .dialogs.audioRecorder import * from .dialogs.audioRecorder import *
from .dialogs.configuration import * from .dialogs.configuration import *
from .dialogs.profiles import *

View File

@ -120,9 +120,9 @@ class mainInfo(wx.Panel):
sizer.Add(sizerOccupation, 0, wx.ALL, 5) sizer.Add(sizerOccupation, 0, wx.ALL, 5)
self.SetSizer(sizer) self.SetSizer(sizer)
class userProfile(widgetUtils.BaseDialog): class userProfileDialog(widgetUtils.BaseDialog):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(userProfile, self).__init__(parent=None, *args, **kwargs) super(userProfileDialog, self).__init__(parent=None, *args, **kwargs)
self.panel = wx.Panel(self) self.panel = wx.Panel(self)
self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer = wx.BoxSizer(wx.VERTICAL)
self.notebook = wx.Notebook(self.panel) self.notebook = wx.Notebook(self.panel)

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import wx
class urlList(wx.Dialog):
def __init__(self):
super(urlList, self).__init__(parent=None, title=_("Select URL"))
panel = wx.Panel(self)
self.lista = wx.ListBox(panel, -1)
self.lista.SetFocus()
self.lista.SetSize(self.lista.GetBestSize())
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.lista, 0, wx.ALL, 5)
goBtn = wx.Button(panel, wx.ID_OK)
goBtn.SetDefault()
cancelBtn = wx.Button(panel, wx.ID_CANCEL)
btnSizer = wx.BoxSizer()
btnSizer.Add(goBtn, 0, wx.ALL, 5)
btnSizer.Add(cancelBtn, 0, wx.ALL, 5)
sizer.Add(btnSizer, 0, wx.ALL, 5)
panel.SetSizer(sizer)
self.SetClientSize(sizer.CalcMin())
def populate_list(self, urls):
for i in urls:
self.lista.Append(i)
self.lista.SetSelection(0)
def get_string(self):
return self.lista.GetStringSelection()
def get_item(self):
return self.lista.GetSelection()
def get_response(self):
return self.ShowModal()