diff --git a/src/controller/mainController.py b/src/controller/mainController.py index 2c223c1..b2f72e5 100644 --- a/src/controller/mainController.py +++ b/src/controller/mainController.py @@ -27,7 +27,6 @@ from . import messages from . import buffers from . import player from . import posts -from . import profiles from . import longpollthread from . import selector @@ -663,7 +662,7 @@ class Controller(object): player.player.volume = 0 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): self.user_profile(self.session.user_id) diff --git a/src/controller/posts.py b/src/controller/posts.py index 5369306..1b3a32d 100644 --- a/src/controller/posts.py +++ b/src/controller/posts.py @@ -17,7 +17,7 @@ import webbrowser import logging from sessionmanager import session, renderers, utils # We'll use some functions from there from pubsub import pub -from wxUI.dialogs import postDialogs, urlList, profiles +from wxUI.dialogs import postDialogs, urlList from extra import SpellChecker, translator from mysc.thread_utils import call_threaded from wxUI import menus @@ -510,107 +510,3 @@ class friendship(object): def set_friends_list(self, friendslist): for i in friendslist: 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"]) \ No newline at end of file diff --git a/src/interactors/__init__.py b/src/interactors/__init__.py index 011323e..40a8280 100644 --- a/src/interactors/__init__.py +++ b/src/interactors/__init__.py @@ -1,2 +1,3 @@ from . audioRecorder import * -from .configuration import * \ No newline at end of file +from .configuration import * +from .profiles import * \ No newline at end of file diff --git a/src/interactors/base.py b/src/interactors/base.py index 535f181..4afcc1a 100644 --- a/src/interactors/base.py +++ b/src/interactors/base.py @@ -11,12 +11,17 @@ class baseInteractor(object): 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.focus_control, "{modulename}_focus_control".format(modulename=modulename)) + pub.subscribe(self.set_title, "{modulename}_set_title".format(modulename=modulename)) def uninstall(self): 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.set_label, "{modulename}_set_label".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): self.view.disable(control) @@ -29,3 +34,6 @@ class baseInteractor(object): def set_label(self, control, label): self.view.set(control, label) + + def set_title(self, value): + self.view.SetTitle(value) \ No newline at end of file diff --git a/src/interactors/profiles.py b/src/interactors/profiles.py new file mode 100644 index 0000000..f1535bb --- /dev/null +++ b/src/interactors/profiles.py @@ -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) \ No newline at end of file diff --git a/src/presenters/__init__.py b/src/presenters/__init__.py index e83f10b..4985bc8 100644 --- a/src/presenters/__init__.py +++ b/src/presenters/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from .audioRecorder import * -from .configuration import * \ No newline at end of file +from .configuration import * +from .profiles import * \ No newline at end of file diff --git a/src/presenters/profiles.py b/src/presenters/profiles.py index ff5a041..4a84a09 100644 --- a/src/presenters/profiles.py +++ b/src/presenters/profiles.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- """ A profile viewer and editor for VK user objects.""" from __future__ import unicode_literals -from future import standard_library -standard_library.install_aliases() -import six import webbrowser import logging import arrow import requests import languageHandler import output -from pubsub import pub +from mysc.thread_utils import call_threaded from sessionmanager import utils from . import base @@ -24,14 +21,17 @@ class userProfilePresenter(base.basePresenter): @user_id integer: User ID to retrieve information of. 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 = None self.session = session self.user_id = user_id - self.get_basic_information() - if self.person != None: - super(profilesPresenter, self).__init__(view=view, interactor=interactor, modulename="profiles") - + # Get information in a threaded way here. + # Note: We do not handle any race condition here because due to the presenter only sending pubsub messages, + # 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): """ Gets and inserts basic user information. @@ -46,26 +46,26 @@ class userProfilePresenter(base.basePresenter): return output.speak(_("Information for groups is not supported, yet.")) person = person[0] # 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. # Format full name. n = "{0} {1}".format(person["first_name"], person["last_name"]) # Format birthdate. 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. if len(person["bdate"]) <= 5: # dd.mm 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 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 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) + self.send_message("enable_control", tab="main_info", control="home_town") + self.send_message("set", tab="main_info", control="home_town", value=home_town) if "city" in person and len(person["city"]) > 0: city = person["city"]["title"] if "country" in person and person["country"] != "": @@ -73,20 +73,19 @@ class userProfilePresenter(base.basePresenter): 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,)) + self.send_message("enable_control", tab="main_info", control="city") + self.send_message("set", tab="main_info", control="city", value=city) + self.send_message("set", tab="main_info", control="name", value=n) + self.send_message("set_title", value=_("{name}'s profile").format(name=n,)) # Format website (or websites, if there are multiple of them). 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) + self.send_message("enable_control", tab="main_info", control="website") + self.send_message("set", tab="main_info", control="website", value=person["site"]) + self.send_message("enable_control", tab="main_info", control="go_site") # Format status message. if "status" in person and person["status"] != "": - self.dialog.main_info.enable("status") - self.dialog.main_info.set("status", person["status"]) + self.send_message("enable_control", tab="main_info", control="status") + self.send_message("set", tab="main_info", control="status", value=person["status"]) # Format occupation. # 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: @@ -97,8 +96,8 @@ class userProfilePresenter(base.basePresenter): c2 = _("In {0}").format(person["occupation"]["name"],) else: c2 = "" - self.dialog.main_info.enable("occupation") - self.dialog.main_info.set("occupation", c1+c2) + self.send_message("enable_control", tab="main_info", control="occupation") + self.send_message("set", tab="main_info", control="occupation", value=c1+c2) # format relationship status. # 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: @@ -119,28 +118,24 @@ class userProfilePresenter(base.basePresenter): r = _("Actively searching") elif person["relation"] == 7: r = _("In love") - self.dialog.main_info.enable("relation") - self.dialog.main_info.relation.SetLabel(_("Relationship: ")+r) + self.send_message("enable_control", tab="main_info", control="relation") + self.send_message("set_label", tab="main_info", control="relation", value=_("Relationship: ")+r) # format last seen. 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) + self.send_message("enable_control", tab="main_info", control="last_seen") + self.send_message("set", tab="main_info", control="last_seen", value=last_seen) self.person = person # Adds photo to the dialog. # ToDo: Need to ask if this has a visible effect in the dialog. if "photo_200_orig" in person: img = requests.get(person["photo_200_orig"]) - image = wx.Image(stream=six.BytesIO(requests.get(person["photo_200_orig"]).content)) - try: - self.dialog.image.SetBitmap(wx.Bitmap(image)) - except ValueError: - return - self.dialog.panel.Layout() + self.send_message("load_image", image=requests.get(person["photo_200_orig"])) + output.speak(_("Profile loaded")) - def visit_website(self, *args, **kwargs): + def get_urls(self, *args, **kwargs): """ Allows to visit an user's website. """ text = self.person["site"] # 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: output.speak(_("No URL addresses were detected.")) return - elif len(urls) == 1: - selected_url = urls[0] - else: - dialog = urlList.urlList() - dialog.populate_list(urls) - if dialog.get_response() != widgetUtils.OK: - return - selected_url = urls[dialog.get_item()] + return urls + + def visit_url(self, url): output.speak(_("Opening URL...")) - webbrowser.open_new_tab(selected_url) \ No newline at end of file + webbrowser.open_new_tab(url) \ No newline at end of file diff --git a/src/views/__init__.py b/src/views/__init__.py index 49d2464..b1da70e 100644 --- a/src/views/__init__.py +++ b/src/views/__init__.py @@ -1,2 +1,3 @@ from .dialogs.audioRecorder import * -from .dialogs.configuration import * \ No newline at end of file +from .dialogs.configuration import * +from .dialogs.profiles import * \ No newline at end of file diff --git a/src/wxUI/dialogs/profiles.py b/src/views/dialogs/profiles.py similarity index 98% rename from src/wxUI/dialogs/profiles.py rename to src/views/dialogs/profiles.py index 74f6c72..205cfeb 100644 --- a/src/wxUI/dialogs/profiles.py +++ b/src/views/dialogs/profiles.py @@ -120,9 +120,9 @@ class mainInfo(wx.Panel): sizer.Add(sizerOccupation, 0, wx.ALL, 5) self.SetSizer(sizer) -class userProfile(widgetUtils.BaseDialog): +class userProfileDialog(widgetUtils.BaseDialog): 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.sizer = wx.BoxSizer(wx.VERTICAL) self.notebook = wx.Notebook(self.panel) diff --git a/src/views/dialogs/urlList.py b/src/views/dialogs/urlList.py new file mode 100644 index 0000000..1c38c62 --- /dev/null +++ b/src/views/dialogs/urlList.py @@ -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() \ No newline at end of file