diff --git a/src/UBUVoiceAssistant/GUI/chat_window.py b/src/UBUVoiceAssistant/GUI/chat_window.py index ef89029..eb3b673 100644 --- a/src/UBUVoiceAssistant/GUI/chat_window.py +++ b/src/UBUVoiceAssistant/GUI/chat_window.py @@ -1,3 +1,5 @@ +"""Module for the chat window GUI +""" from threading import Thread import subprocess import sys @@ -16,11 +18,13 @@ class ChatWindow(QtWidgets.QMainWindow): - def __init__(self, bus: MessageBusClient, ws: WebService) -> None: + """Class for the Chat window GUI + """ + def __init__(self, bus: MessageBusClient, webservice: WebService) -> None: super().__init__(parent=None) uic.loadUi("./UBUVoiceAssistant/GUI/forms/chat-window.ui", self) self.bus = bus - self.ws = ws + self.webservice = webservice self.user_utterance = "" self.mycroft_response = "" self.mic_muted = False @@ -29,8 +33,10 @@ def __init__(self, bus: MessageBusClient, ws: WebService) -> None: self.next_message = 0 self.intent_labels = [] - self.mic_icon = QtGui.QIcon(QtGui.QPixmap("UBUVoiceAssistant/imgs/mic.svg")) - self.mic_muted_icon = QtGui.QIcon(QtGui.QPixmap("UBUVoiceAssistant/imgs/mic_muted.svg")) + self.mic_icon = QtGui.QIcon( + QtGui.QPixmap("UBUVoiceAssistant/imgs/mic.svg")) + self.mic_muted_icon = QtGui.QIcon( + QtGui.QPixmap("UBUVoiceAssistant/imgs/mic_muted.svg")) self.color: List[Union[int, float]] = list( self.palette().color(QtGui.QPalette.ColorRole.Background).getRgb()) @@ -73,6 +79,8 @@ def __init__(self, bus: MessageBusClient, ws: WebService) -> None: # os.path.abspath(os.getcwd())+"/UBUVoiceAssistant/GUI/forms/chat_window_html")) def set_elements_web(self): + """Sets the initial elements for the HTML webview + """ print(self.color) js_color = "document.body.style.backgroundColor = 'rgba(" + ','.join( map(str, self.color)) + ")';" @@ -84,7 +92,8 @@ def set_elements_web(self): message += "· " + _("Tell me about the forums of (course)\n") message += "· " + _("Tell me my grades\n") message += "· " + _("Tell me about the events of (course)\n") - message += "· " + _("Tell me about the events on (month) (day) (year)\n") + message += "· " + \ + _("Tell me about the events on (month) (day) (year)\n") message += "· " + _("Tell me about the changes of (course)\n") message += "· " + _("Tell me the grades of (course)\n") message += "· " + _("Read the latest messages\n") @@ -99,11 +108,19 @@ def set_elements_web(self): self.web.page().runJavaScript(js_string) def update_texts(self): + """Sets the localized texts for the UI + """ self.btnConfig.setText(_("Manage skills")) self.btnSend.setText(_("Send")) self.tbxInput.setPlaceholderText(_("Type your command here...")) def update_chat(self, source: str, message: str): + """Adds the new message to the chat + + Args: + source (str): "u" when coming from user, "r" when coming from Mycroft + message (str): The message text + """ js_string = "var chat = document.getElementById('chat-window');\n" js_string += "var msg = document.createElement('li');\n" if source == "u": @@ -116,12 +133,24 @@ def update_chat(self, source: str, message: str): self.web.page().runJavaScript(js_string) def handle_speak(self, message: Message): + """Sets the Mycroft response variable when a message comes + + Args: + message (Message): Mycroft bus message + """ self.mycroft_response = message.data.get("utterance") def handle_utterance(self, message: Message): + """Sets the user message variable when you send a message + + Args: + message (Message): User bus message + """ self.user_utterance = message.data["utterances"][0] def check_for_chat_update(self): + """Checks if a new message arrives and updates the UI + """ if self.user_utterance: self.update_chat("u", self.user_utterance) self.user_utterance = "" @@ -130,6 +159,11 @@ def check_for_chat_update(self): self.mycroft_response = "" def closeEvent(self, event: QtGui.QCloseEvent) -> None: + """Handles when the user tries to close the window + + Args: + event (QtGui.QCloseEvent): Qt Close Event + """ self.close_window = MessageBox(_("Are you sure?")) self.close_window.setStandardButtons( QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel) @@ -148,19 +182,33 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None: event.ignore() def finish_exit(self): + """Exits the program + """ sys.exit(0) def keyPressEvent(self, event): + """This is executed when the user press a key + + Args: + event: Qt Keypress event + """ if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): self.on_send_pressed() def on_send_pressed(self): + """This runs when the user press enter or clicks the button on the UI + """ self.user_utterance = self.tbxInput.text() self.bus.emit(Message('recognizer_loop:utterance', { 'utterances': [self.user_utterance]})) self.tbxInput.setText('') def on_mic_pressed(self, startup: bool = False): + """This runs when the user clicks the mic button + + Args: + startup (bool, optional): True if this is running at startup. Defaults to False. + """ # Switch between muted and unmuted when the mic is pressed if self.mic_muted or startup: self.mic_muted = False @@ -176,14 +224,19 @@ def on_mic_pressed(self, startup: bool = False): self.bus.emit(Message('mycroft.mic.mute')) def on_skills_pressed(self): + """This runs when you press the "manage skills" button. Shows the new window + """ self.skills_dialog = QtWidgets.QDialog(self) self.skills_dialog.setWindowTitle('Mycroft Skills') self.skills_dialog.resize(600, 600) - self.pushButton_manage_skills = QtWidgets.QPushButton(self.skills_dialog) - self.pushButton_manage_skills.setGeometry(QtCore.QRect(470, 10, 120, 40)) - self.pushButton_manage_skills.clicked.connect(self.on_manage_skills_pressed) + self.pushButton_manage_skills = QtWidgets.QPushButton( + self.skills_dialog) + self.pushButton_manage_skills.setGeometry( + QtCore.QRect(470, 10, 120, 40)) + self.pushButton_manage_skills.clicked.connect( + self.on_manage_skills_pressed) self.pushButton_manage_skills.setText(_("Save")) @@ -202,14 +255,17 @@ def on_skills_pressed(self): # Create checkboxes for every skill in self.active_skills for count, name in enumerate(self.active_skills): check_box = QtWidgets.QCheckBox(scroll_area_widget_skills) - spacer = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + spacer = QtWidgets.QSpacerItem( + 40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) check_box.setText(name) check_box.setChecked(True) logo = QtWidgets.QLabel(scroll_area_widget_skills) if 'ubu' in name: - logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20)) + logo.setPixmap(QtGui.QPixmap( + 'UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20)) else: - logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20)) + logo.setPixmap(QtGui.QPixmap( + 'UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20)) self.active_skills_checkBoxes.append(check_box) skills_grid_layout.addWidget(logo, count, 0) skills_grid_layout.addWidget(check_box, count, 1) @@ -221,9 +277,11 @@ def on_skills_pressed(self): check_box.setText(name) logo = QtWidgets.QLabel(scroll_area_widget_skills) if 'ubu' in name: - logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20)) + logo.setPixmap(QtGui.QPixmap( + 'UBUVoiceAssistant/imgs/ubu_logo.jpg').scaled(20, 20)) else: - logo.setPixmap(QtGui.QPixmap('UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20)) + logo.setPixmap(QtGui.QPixmap( + 'UBUVoiceAssistant/imgs/Mycroft_logo.png').scaled(20, 20)) self.inactive_skills_checkBoxes.append(check_box) skills_grid_layout.addWidget(logo, count, 0) skills_grid_layout.addWidget(check_box, count, 1) @@ -233,24 +291,28 @@ def on_skills_pressed(self): def on_manage_skills_pressed(self): """ Adds the checked skills to self.active_skills and the unchecked to - self.inactive_skills and activates or deactivates those skills. + self.inactive_skills and activates or deactivates those skills when pressing save. """ deactivated = [] activated = [] - for cb in self.active_skills_checkBoxes: - if not cb.isChecked(): - self.bus.emit(Message('skillmanager.deactivate', {'skill': cb.text()})) - deactivated.append(cb.text()) - - for cb in self.inactive_skills_checkBoxes: - if cb.isChecked(): - self.bus.emit(Message('skillmanager.activate', {'skill': cb.text()})) - activated.append(cb.text()) - - self.active_skills = [skill for skill in self.active_skills if skill not in deactivated] + for checkbox in self.active_skills_checkBoxes: + if not checkbox.isChecked(): + self.bus.emit( + Message('skillmanager.deactivate', {'skill': checkbox.text()})) + deactivated.append(checkbox.text()) + + for checkbox in self.inactive_skills_checkBoxes: + if checkbox.isChecked(): + self.bus.emit( + Message('skillmanager.activate', {'skill': checkbox.text()})) + activated.append(checkbox.text()) + + self.active_skills = [ + skill for skill in self.active_skills if skill not in deactivated] self.active_skills.extend(activated) - self.inactive_skills = [skill for skill in self.inactive_skills if skill not in activated] + self.inactive_skills = [ + skill for skill in self.inactive_skills if skill not in activated] self.inactive_skills.extend(deactivated) self.skills_dialog.hide() @@ -258,6 +320,9 @@ def on_manage_skills_pressed(self): class CloseMycroft(QtCore.QThread): + """Thread to close Mycroft""" def run(self): + """Closes Mycroft and emits a signal when finished + """ subprocess.run("/usr/lib/mycroft-core/stop-mycroft.sh", shell=True) self.finished.emit() diff --git a/src/UBUVoiceAssistant/GUI/link_mycroft.py b/src/UBUVoiceAssistant/GUI/link_mycroft.py index 8ffceb1..9892940 100644 --- a/src/UBUVoiceAssistant/GUI/link_mycroft.py +++ b/src/UBUVoiceAssistant/GUI/link_mycroft.py @@ -18,7 +18,7 @@ class LinkMycroft(QtWidgets.QMainWindow): """Class for the linking Mycroft to web UI """ - + closed_signal = pyqtSignal() def __init__(self, bus: MessageBusClient) -> None: @@ -45,9 +45,10 @@ def __init__(self, bus: MessageBusClient) -> None: self.file = open("/var/log/mycroft/skills.log", "r") self.file.seek(0, 2) # Goes to the end of the file - self.bus.emit(Message("recognizer_loop:utterance", - {'utterances': [_("pair my device")]})) - # On other languages different than English, we must send again the phrase for it to start pairing + self.bus.emit(Message("recognizer_loop:utterance", + {'utterances': [_("pair my device")]})) + # On other languages different than English, we must send again the phrase for it + # to start pairing bus.on("configuration.updated", self.pairing_done) self.timer = QTimer() self.timer.timeout.connect(self.add_pairing_code) # type: ignore @@ -58,21 +59,31 @@ def __init__(self, bus: MessageBusClient) -> None: self.setFixedSize(self.size()) def pairing_done(self, event): + """This function gets triggered when we finish pairing Mycroft. + + Args: + event: Mycroft pairing event + """ self.done = True self.file.close() self.close() def add_pairing_code(self): + """Sets the pairing code and shows it on the UI + """ self.lblCode.setText(self.code) def read_pairing_code(self): + """Reads the skills file and sets the pairing code to a variable + """ while not self.done: print("Reading...") line = self.file.readline() if line: print(line) matches = re.findall( - "(?<=" + re.escape("PairingSkill | ") + ")[a-zA-Z0-9 ]*: [A-Z0-9]{6}(?=\n)", line) + "(?<=" + re.escape("PairingSkill | ") + ")[a-zA-Z0-9 ]*: [A-Z0-9]{6}(?=\n)", + line) print(matches) if matches: self.code = matches[0][-6:] @@ -80,8 +91,11 @@ def read_pairing_code(self): time.sleep(1) def update_texts(self): + """Sets the localized texts in the UI + """ self.lblWelcome.setText(_("Welcome!")) - self.lblFirstRun.setText(_("It's your first time using Mycroft, so please follow these instructions")) + self.lblFirstRun.setText( + _("It's your first time using Mycroft, so please follow these instructions")) self.btnPrev.setText(_("Previous")) self.btnNext.setText(_("Next")) self.lblRegisterAddDevice.setText( @@ -90,6 +104,8 @@ def update_texts(self): self.lblInfoCode.setText(_("Input this code on the website")) def go_next(self): + """This function gets executed when the user clicks the next button + """ self.hide_all_elements() self.page = min(2, self.page + 1) if self.page == 1: @@ -105,6 +121,8 @@ def go_next(self): self.btnPrev.setEnabled(True) def go_previous(self): + """This function gets executed when the user click the previous version + """ self.hide_all_elements() self.page = max(0, self.page - 1) if self.page == 1: @@ -119,6 +137,8 @@ def go_previous(self): self.btnNext.setEnabled(True) def hide_all_elements(self): + """Hides all images and texts when switching pages + """ self.imgSelectVoice.setVisible(False) self.imgAddDevice.setVisible(False) self.picInputCode.setVisible(False) @@ -132,6 +152,11 @@ def hide_all_elements(self): self.btnNext.setEnabled(False) def closeEvent(self, event: QtGui.QCloseEvent) -> None: + """This get triggered when closing the window + + Args: + event (QtGui.QCloseEvent): Qt Close event + """ if self.done: self.closed_signal.emit() time.sleep(2) # type: ignore @@ -144,7 +169,8 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None: print(self.close_res) if self.close_res == QtWidgets.QMessageBox.Yes: self.timer.stop() - self.closing_window = ProgressBox(_("Closing Mycroft, please wait...")) + self.closing_window = ProgressBox( + _("Closing Mycroft, please wait...")) self.closing_window.show() self.closing_thread = CloseMycroft() self.closing_thread.finished.connect( # type: ignore @@ -154,10 +180,16 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None: event.ignore() def finish_exit(self): + """Exits the program + """ sys.exit(0) class CloseMycroft(QThread): + """A thread to close Mycroft when exiting + """ def run(self): + """Closes Mycroft and emits the event + """ subprocess.run("/usr/lib/mycroft-core/stop-mycroft.sh", shell=True) self.finished.emit() diff --git a/src/UBUVoiceAssistant/GUI/main.py b/src/UBUVoiceAssistant/GUI/main.py index ddeb5ef..00709d6 100644 --- a/src/UBUVoiceAssistant/GUI/main.py +++ b/src/UBUVoiceAssistant/GUI/main.py @@ -1,24 +1,24 @@ """Module for the main UI """ +import subprocess import sys -import time +from os import path +from threading import Thread -from PyQt5.QtGui import QIcon -from ..GUI.link_mycroft import LinkMycroft import requests -import subprocess import simplejson -from threading import Thread -from os import path -from PyQt5 import QtWidgets, uic, QtCore from mycroft_bus_client import MessageBusClient -from ..webservice.web_service import WebService -from .message_box import MessageBox -from .progress_box import ProgressBox +from PyQt5 import QtCore, QtWidgets, uic +from PyQt5.QtGui import QIcon + +from ..GUI.link_mycroft import LinkMycroft from ..util.lang import Translator -from ..util.util import create_server_socket from ..util.settings import Settings +from ..util.util import create_server_socket +from ..webservice.web_service import WebService from .chat_window import ChatWindow +from .message_box import MessageBox +from .progress_box import ProgressBox translator = Translator() _ = translator.translate @@ -69,6 +69,8 @@ def on_lang_changed(self, value: int) -> None: self.update_texts() def on_login(self): + """This runs when the user clicks the login button or press enter + """ user = str(self.tbxUser.text()) password = self.tbxPassword.text() host = str(self.tbxHost.text()) @@ -76,11 +78,11 @@ def on_login(self): if not host: host = "https://ubuvirtual.ubu.es" - self.ws = WebService() - self.ws.set_host(host) + self.webservice = WebService() + self.webservice.set_host(host) try: - self.ws.set_url_with_token(user, password) + self.webservice.set_url_with_token(user, password) # If the credentials are incorrect except KeyError: MessageBox(_("Invalid Moodle username or password.")).exec_() @@ -91,24 +93,24 @@ def on_login(self): except (requests.exceptions.ConnectionError, simplejson.errors.JSONDecodeError): MessageBox(_("Connection error. Please check that the URL is correct, your Internet connection is working and the server is up.")).exec_() return - + if self.chkUser.isChecked(): self.cfg["user"] = user if self.chkHost.isChecked(): self.cfg["host"] = host self.cfg["lang"] = translator.get_current_language()[0] self.cfg.save_settings() - self.ws.initialize_useful_data() + self.webservice.initialize_useful_data() # If Moodle lang is different from the selected - if not translator.check_language_supported(self.ws.get_lang()): + if not translator.check_language_supported(self.webservice.get_lang()): MessageBox(_("This language is not supported by your Moodle server")).exec_() - self.ws.set_user_courses() + self.webservice.set_user_courses() self.starting_window = ProgressBox(_("Starting Mycroft, please wait...")) self.starting_window.show() - server_socket = Thread(target=create_server_socket, args=[self.ws]) + server_socket = Thread(target=create_server_socket, args=[self.webservice]) server_socket.setDaemon(True) server_socket.start() @@ -129,6 +131,8 @@ def update_texts(self) -> None: self.lblUser.setText(_("Username")) def start_mycroft(self): + """Starts Mycroft and checks for when it's ready + """ def f_mycroft_started(event): self.mycroft_started = True @@ -158,17 +162,24 @@ def check_mycroft_started(self): self.new_window.closed_signal.connect(self.check_mycroft_started) else: if not self.finished: - self.new_window = ChatWindow(self.bus, self.ws) + self.new_window = ChatWindow(self.bus, self.webservice) self.new_window.show() self.new_window.tbxInput.setFocus() self.hide() self.finished = True def set_reconnect_1s(self, event = None): + """Set the bus reconnect time to 1 second + """ print("Set reconnect time") self.bus.retry = 0.5 def keyPressEvent(self, event): + """This happens when the user press a key + + Args: + event: Qt Keypress event + """ if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): self.on_login() diff --git a/src/UBUVoiceAssistant/GUI/progress_box.py b/src/UBUVoiceAssistant/GUI/progress_box.py index 4a6a0cf..eccbc6f 100644 --- a/src/UBUVoiceAssistant/GUI/progress_box.py +++ b/src/UBUVoiceAssistant/GUI/progress_box.py @@ -1,6 +1,6 @@ """Module for infinite progress bars """ -from PyQt5 import QtWidgets, QtCore +from PyQt5 import QtWidgets class ProgressBox(QtWidgets.QProgressDialog): @@ -17,6 +17,12 @@ def __init__(self, text: str): self.done = False def closeEvent(self, event) -> None: + """This event gets triggered when trying to close the Window. We ignore them if we haven't + finished yet + + Args: + event: Qt Close event + """ if not self.done: event.ignore() else: diff --git a/src/UBUVoiceAssistant/model/conversation.py b/src/UBUVoiceAssistant/model/conversation.py index 2f7d57d..b7e2334 100644 --- a/src/UBUVoiceAssistant/model/conversation.py +++ b/src/UBUVoiceAssistant/model/conversation.py @@ -1,13 +1,15 @@ +"""Module for the conversation class""" from typing import Dict from .user import User from .message import Message class Conversation(): + """Class for the Moodle conversations""" def __init__(self, json: dict) -> None: self.__conversation_id: int = json["id"] self.__isread: bool = json["isread"] - if self.__isread == True: + if self.__isread is True: self.__unreadcount = 0 else: self.__unreadcount = json["unreadcount"] @@ -25,22 +27,57 @@ def __init__(self, json: dict) -> None: self.__messages[message["id"]] = Message(message) def get_conversation_id(self) -> int: + """Gets the conversation id + + Returns: + int: conversation id + """ return self.__conversation_id def get_isread(self) -> bool: + """Gets if the conversation is read + + Returns: + bool: true if is read, false if not + """ return self.__isread def get_unreadcount(self) -> int: + """Gets the number of unread messages + + Returns: + int: unread messages count + """ return self.__unreadcount def get_name(self) -> str: + """Gets the name for the conversation + + Returns: + str: conversation name + """ return self.__name - + def get_subname(self) -> str: + """Gets the subname for the conversation + + Returns: + str: conversation subname + """ return self.__subname def get_members(self) -> Dict[int, User]: + """Gets the members of the conversation + + Returns: + Dict[int, User]: Dictionary userid, user + """ return self.__members - + def get_messages(self) -> Dict[int, Message]: - return self.__messages \ No newline at end of file + """Gets the messages in a conversation + + Returns: + Dict[int, Message]: Dictionary messageid, message + """ + return self.__messages diff --git a/src/UBUVoiceAssistant/model/course.py b/src/UBUVoiceAssistant/model/course.py index 3f7f1e9..b069a9e 100644 --- a/src/UBUVoiceAssistant/model/course.py +++ b/src/UBUVoiceAssistant/model/course.py @@ -27,7 +27,7 @@ def __init__(self, course): self.__forums = [] self.__participants: Dict[int, "User"] = {} - def get_id(self) -> int: # TODO revisar + def get_id(self) -> int: """Gets the Course id Returns: @@ -92,13 +92,22 @@ def set_forums(self, forums: List[Forum]): self.__forums = forums def set_participants(self, participants: Union[List["User"], Dict[int, "User"]]): - if isinstance(participants, List): + """Sets the participants of the course + + Args: + participants (List[User] or Dict[int, User]): List of users or dictionary of userid, user + """ + if isinstance(participants, list): self.__participants = {} - for p in participants: - self.__participants[p.get_id()] = p + for participant in participants: + self.__participants[participant.get_id()] = participant return - else: - self.__participants = participants - + self.__participants = participants + def get_participants(self) -> Dict[int, "User"]: + """Gets the list of participants + + Returns: + Dict[int, User]: Dictionary of userid, user + """ return self.__participants diff --git a/src/UBUVoiceAssistant/model/discussion.py b/src/UBUVoiceAssistant/model/discussion.py index c32e70a..0fd5dd4 100644 --- a/src/UBUVoiceAssistant/model/discussion.py +++ b/src/UBUVoiceAssistant/model/discussion.py @@ -15,7 +15,7 @@ def __init__(self, discussion): self.__discussion_id = discussion['discussion'] self.__name = discussion['name'] - def get_id(self) -> int: # TODO Check type + def get_id(self) -> int: """Gets the id of the discussion Returns: diff --git a/src/UBUVoiceAssistant/model/forum.py b/src/UBUVoiceAssistant/model/forum.py index 3c26a37..561eb75 100644 --- a/src/UBUVoiceAssistant/model/forum.py +++ b/src/UBUVoiceAssistant/model/forum.py @@ -20,7 +20,7 @@ def __init__(self, forum, discussions): self.__name = forum['name'] self.__discussions = discussions - def get_id(self) -> int: # TODO Revisar si es correcto + def get_id(self) -> int: """Gets the id of the Forum Returns: diff --git a/src/UBUVoiceAssistant/model/grade_item.py b/src/UBUVoiceAssistant/model/grade_item.py index 50c0c10..a5daa5f 100644 --- a/src/UBUVoiceAssistant/model/grade_item.py +++ b/src/UBUVoiceAssistant/model/grade_item.py @@ -16,11 +16,11 @@ def __init__(self, grade): self.__name = grade['itemname'] self.__type = grade['itemtype'] - def get_value(self): # TODO Check type of value + def get_value(self): """Gets the grade value Returns: - [type]: Value of the grade + int, str: Value of the grade """ return self.__value @@ -32,11 +32,11 @@ def get_name(self) -> str: """ return self.__name - def get_type(self): # TODO Check type of type + def get_type(self): """Gets the type of grade Returns: - [type]: type of grade + type of grade """ return self.__type diff --git a/src/UBUVoiceAssistant/model/message.py b/src/UBUVoiceAssistant/model/message.py index 067a95f..d2c48d9 100644 --- a/src/UBUVoiceAssistant/model/message.py +++ b/src/UBUVoiceAssistant/model/message.py @@ -1,6 +1,11 @@ +"""Module for the message class +""" from bs4 import BeautifulSoup + class Message(): + """Class for the messages + """ def __init__(self, json: dict) -> None: self.__message_id: int = json["id"] self.__useridfrom: int = json["useridfrom"] @@ -8,16 +13,41 @@ def __init__(self, json: dict) -> None: self.__timecreated: int = json["timecreated"] def get_message_id(self) -> int: + """Gets the message id + + Returns: + int: message id + """ return self.__message_id - + def get_useridfrom(self) -> int: + """Gets the user id who sent this message + + Returns: + int: user id + """ return self.__useridfrom - + def get_text(self) -> str: + """Gets the raw text of the message + + Returns: + str: text + """ return self.__text def get_timecreated(self) -> int: + """Gets the timestamp of the message + + Returns: + int: unix time of the message + """ return self.__timecreated def get_clean_text(self) -> str: - return BeautifulSoup(self.__text, "html.parser").get_text() \ No newline at end of file + """Gets the text of the message, without any HTML tags + + Returns: + str: text + """ + return BeautifulSoup(self.__text, "html.parser").get_text() diff --git a/src/UBUVoiceAssistant/model/user.py b/src/UBUVoiceAssistant/model/user.py index 28967d7..22f1465 100644 --- a/src/UBUVoiceAssistant/model/user.py +++ b/src/UBUVoiceAssistant/model/user.py @@ -21,7 +21,7 @@ def __init__(self, user: Union[int, dict]): """ self.__courses: Dict[int, Course] = {} self.__final_grades: List[GradeItem] = [] - if isinstance(user, int) or isinstance(user, str): + if isinstance(user, (int, str)): self.__user_id = user self.__fullname = "" else: @@ -89,7 +89,17 @@ def set_final_grades(self, grades: List[GradeItem]): self.__final_grades = grades def set_fullname(self, name: str): + """Sets the user fullname + + Args: + name (str): fullname + """ self.__fullname = name def get_fullname(self) -> str: + """Gets the user fullname + + Returns: + str: fullname + """ return self.__fullname diff --git a/src/UBUVoiceAssistant/skills/ubu-calendar b/src/UBUVoiceAssistant/skills/ubu-calendar index fa717ef..d88de64 160000 --- a/src/UBUVoiceAssistant/skills/ubu-calendar +++ b/src/UBUVoiceAssistant/skills/ubu-calendar @@ -1 +1 @@ -Subproject commit fa717ef59e4522baac72e16d7b2331de9e0c7ed2 +Subproject commit d88de64bbb76cdd92ec656167f19d31aec6d1be9 diff --git a/src/UBUVoiceAssistant/skills/ubu-course b/src/UBUVoiceAssistant/skills/ubu-course index b922c2b..055f4d7 160000 --- a/src/UBUVoiceAssistant/skills/ubu-course +++ b/src/UBUVoiceAssistant/skills/ubu-course @@ -1 +1 @@ -Subproject commit b922c2b9d82453f62b35f04c3fe8e8d565b32ed6 +Subproject commit 055f4d704bf17e380196635d2dff7603c8832a56 diff --git a/src/UBUVoiceAssistant/skills/ubu-grades b/src/UBUVoiceAssistant/skills/ubu-grades index 6a948a5..233a3f2 160000 --- a/src/UBUVoiceAssistant/skills/ubu-grades +++ b/src/UBUVoiceAssistant/skills/ubu-grades @@ -1 +1 @@ -Subproject commit 6a948a5ea9e440ee2f2c06aa82e7f142d2c77da2 +Subproject commit 233a3f2c1e9d9d00e2c135ff28dc5db08a301960 diff --git a/src/UBUVoiceAssistant/skills/ubu-help b/src/UBUVoiceAssistant/skills/ubu-help index 4167150..008cdee 160000 --- a/src/UBUVoiceAssistant/skills/ubu-help +++ b/src/UBUVoiceAssistant/skills/ubu-help @@ -1 +1 @@ -Subproject commit 416715095e1a448f6635b6662dfca743ab9a1ff8 +Subproject commit 008cdee8c366f844cb6ebd845f1512d566b103df diff --git a/src/UBUVoiceAssistant/skills/ubu-messages b/src/UBUVoiceAssistant/skills/ubu-messages index 24b760c..a91a18a 160000 --- a/src/UBUVoiceAssistant/skills/ubu-messages +++ b/src/UBUVoiceAssistant/skills/ubu-messages @@ -1 +1 @@ -Subproject commit 24b760cb099990321e886b1e09e1e00a25a0b7a2 +Subproject commit a91a18a8d1126ab1f146a5c183a5fa541b45dde8 diff --git a/src/UBUVoiceAssistant/util/lang.py b/src/UBUVoiceAssistant/util/lang.py index ba8cd40..5b7ddcd 100644 --- a/src/UBUVoiceAssistant/util/lang.py +++ b/src/UBUVoiceAssistant/util/lang.py @@ -102,7 +102,23 @@ def update_mycroft_config(self) -> None: json.dump(mycroft_cfg, mycroft_cfg_file) def get_language_index(self, lang_code: str) -> int: + """Gets the position for the language + + Args: + lang_code (str): Language code, similar to es_ES + + Returns: + int: The position in the list of loaded languages + """ return self._available_langs.index(lang_code) def check_language_supported(self, lang: str) -> bool: + """Checks if the language is supported on Moodle + + Args: + lang (str): Language code from Moodle, similar to es + + Returns: + bool: true if the language is supported, false if not + """ return lang == self.get_current_language()[0].split("_")[0] diff --git a/src/UBUVoiceAssistant/util/singleton.py b/src/UBUVoiceAssistant/util/singleton.py index cc5e590..e198107 100644 --- a/src/UBUVoiceAssistant/util/singleton.py +++ b/src/UBUVoiceAssistant/util/singleton.py @@ -1,3 +1,5 @@ +"""Module for the singleton class +""" class Singleton(type): # https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python """Singleton class """ diff --git a/src/UBUVoiceAssistant/util/util.py b/src/UBUVoiceAssistant/util/util.py index a9e98f3..d23c72a 100644 --- a/src/UBUVoiceAssistant/util/util.py +++ b/src/UBUVoiceAssistant/util/util.py @@ -3,7 +3,6 @@ import pickle import socket import re -from os import environ from typing import List, Optional from ..model.course import Course from .settings import Settings @@ -16,13 +15,14 @@ 'ú': 'ú', '\xa0': ' '} -def create_server_socket(unserialized_data, host=SOCKET_HOST, port=SOCKET_PORT): # TODO Document - """Creates a server socket +def create_server_socket(unserialized_data, host=SOCKET_HOST, port=SOCKET_PORT): + """Creates a server socket. This is used to send the webservice object from the frontend + to the backend Args: - unserialized_data ([type]): [description] - host ([type], optional): [description]. Defaults to SOCKET_HOST. - port ([type], optional): [description]. Defaults to SOCKET_PORT. + unserialized_data ([type]): unserialized data to send + host ([type], optional): ip to bind the socket to. Defaults to SOCKET_HOST. + port ([type], optional): port to listen. Defaults to SOCKET_PORT. """ server_socket = socket.socket() server_socket.bind((host, port)) @@ -33,15 +33,15 @@ def create_server_socket(unserialized_data, host=SOCKET_HOST, port=SOCKET_PORT): client_socket.send(data) -def get_data_from_server(host=SOCKET_HOST, port=SOCKET_PORT): # TODO Document - """Gets data from the server. +def get_data_from_server(host=SOCKET_HOST, port=SOCKET_PORT): + """Gets data from the server. This is used to get the webservice object in the skills Args: - host ([type], optional): [description]. Defaults to SOCKET_HOST. - port ([type], optional): [description]. Defaults to SOCKET_PORT. + host ([type], optional): host where it's the socket. Defaults to SOCKET_HOST. + port ([type], optional): port where it's the socket. Defaults to SOCKET_PORT. Returns: - [type]: [description] + Unserialized data. This will usually be a webservice """ client_socket = socket.socket() client_socket.connect((host, port)) @@ -87,7 +87,7 @@ def translate_moodle_words(string: str) -> str: return string -def get_course_id_by_name(course_to_find: str, user_courses: List[Course]) -> Optional[Course]: +def get_course_id_by_name(course_to_find: str, user_courses: List[Course]) -> Optional[str]: """Finds a course by its name Args: @@ -104,6 +104,15 @@ def get_course_id_by_name(course_to_find: str, user_courses: List[Course]) -> Op def reorder_name(fullname: str) -> str: + """Reorders the name from "surname, name" to "name surname". Doesn't change anything if there + is no comma in the text. + + Args: + fullname (str): fullname to reorder + + Returns: + str: ordered fullname + """ try: lastname, firstname = fullname.split(", ") return " ".join([firstname, lastname]) diff --git a/src/UBUVoiceAssistant/webservice/web_service.py b/src/UBUVoiceAssistant/webservice/web_service.py index 9804d4a..1c56e9a 100644 --- a/src/UBUVoiceAssistant/webservice/web_service.py +++ b/src/UBUVoiceAssistant/webservice/web_service.py @@ -1,6 +1,6 @@ """WebService file """ -from typing import Dict, List, Optional, Union +from typing import Dict, List, Union import requests @@ -249,6 +249,11 @@ def get_forum_discussion_posts(self, discussionid): return req def get_conversations(self) -> List[Conversation]: + """Gets the list of conversations for the user + + Returns: + List[Conversation]: List of conversations + """ url = (self.__url_with_token + "core_message_get_conversations") params = {"userid": self.get_user().get_id()} req = requests.get(url, params).json() @@ -258,13 +263,18 @@ def get_conversations(self) -> List[Conversation]: return result def get_conversations_with_messages(self) -> List[Conversation]: - convers = self.get_conversations() + """Gets the list of conversations with messages for the user + + Returns: + List[Conversation]: List of conversations + """ + conversations = self.get_conversations() url = (self.__url_with_token + "core_message_get_conversation") result: List[Conversation] = [] - for c in convers: + for conversation in conversations: params = { "userid": self.get_user().get_id(), - "conversationid": c.get_conversation_id(), + "conversationid": conversation.get_conversation_id(), "includecontactrequests": 0, "includeprivacyinfo": 0 } @@ -273,24 +283,44 @@ def get_conversations_with_messages(self) -> List[Conversation]: return result def send_message_to_conversation(self, message: str, conversation: int): + """Sends a message to an already existing conversation + + Args: + message (str): Message text + conversation (int): Conversation id + """ url = (self.__url_with_token + "core_message_send_messages_to_conversation") params: Dict[str, Union[str, int]] = { "conversationid": conversation, "messages[0][text]": message, "messages[0][textformat]": 2 # PLAIN } - req = requests.get(url, params).json() + requests.get(url, params) def send_message_to_user(self, message: str, user: int): + """Sends a message to another user + + Args: + message (str): Message text + user (int): User id + """ url = (self.__url_with_token + "core_message_send_instant_messages") params: Dict[str, Union[str, int]] = { "messages[0][touserid]": user, "messages[0][text]": message, "messages[0][textformat]": 2 # PLAIN } - req = requests.get(url, params).json() + requests.get(url, params) def check_can_message_user(self, user: int) -> bool: + """Checks if the user can message another user + + Args: + user (int): User id + + Returns: + bool: True if you can message that user, false if not + """ url = (self.__url_with_token + "core_message_get_member_info") params = { "referenceuserid": self.__user.get_id(), @@ -301,6 +331,15 @@ def check_can_message_user(self, user: int) -> bool: return bool(req[0]["canmessage"]) def get_participants_by_course(self, course: int) -> List[User]: + """Gets the list of participants in a course. In UBUVirtual, if you are a student, you + can only see the teachers. + + Args: + course (int): Course id + + Returns: + List[User]: List of participants + """ url = (self.__url_with_token + "core_enrol_get_enrolled_users") params = {"courseid": course} req = requests.get(url, params).json()