From e8e15c6882385d551b94dbedc002b75ffad03f17 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Tue, 21 Feb 2017 22:41:47 +0300 Subject: [PATCH 01/15] feat(progress): added torrent download progress --- monitorrent/plugin_managers.py | 6 ++ monitorrent/plugins/clients/__init__.py | 8 ++ monitorrent/plugins/clients/deluge.py | 18 +++++ monitorrent/plugins/clients/downloader.py | 4 + monitorrent/plugins/clients/qbittorrent.py | 15 ++++ monitorrent/plugins/clients/transmission.py | 14 ++++ monitorrent/plugins/clients/utorrent.py | 18 +++++ monitorrent/rest/clients.py | 12 +++ server.py | 3 +- ...ginTest.test_get_download_status_not_found | 52 +++++++++++++ ...luginTest.test_get_download_status_success | 55 ++++++++++++++ tests/plugins/clients/test_deluge_plugin.py | 52 +++++++++++++ tests/plugins/clients/test_downloader.py | 8 ++ tests/plugins/clients/test_qbittorrent.py | 76 +++++++++++++++++++ .../clients/test_transmission_plugin.py | 42 +++++++++- tests/plugins/clients/test_utorrent_plugin.py | 31 ++++++++ tests/rest/test_api_clients.py | 26 ++++++- tests/test_plugin_managers_clients_manager.py | 14 ++++ 18 files changed, 450 insertions(+), 4 deletions(-) create mode 100644 tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found create mode 100644 tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success diff --git a/monitorrent/plugin_managers.py b/monitorrent/plugin_managers.py index 7ef47969..0d5bb453 100644 --- a/monitorrent/plugin_managers.py +++ b/monitorrent/plugin_managers.py @@ -204,6 +204,12 @@ def find_torrent(self, torrent_hash): result = self.default_client.find_torrent(torrent_hash) return result or False + def get_download_status(self, torrent_hash): + if self.default_client is None: + return False + result = self.default_client.get_download_status(torrent_hash) + return result or False + def add_torrent(self, torrent, topic_settings): """ :type torrent: str diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py index 1460e513..c17037ab 100644 --- a/monitorrent/plugins/clients/__init__.py +++ b/monitorrent/plugins/clients/__init__.py @@ -1,6 +1,14 @@ from monitorrent.plugins.trackers import Topic +class DownloadStatus(object): + def __init__(self, downloaded_bytes, total_bytes, download_speed, upload_speed): + self.downloaded_bytes = downloaded_bytes + self.total_bytes = total_bytes + self.download_speed = download_speed + self.upload_speed = upload_speed + + class TopicSettings(object): download_dir = None diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index 69c0c8de..df067b31 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -7,6 +7,8 @@ from monitorrent.plugin_managers import register_plugin from datetime import datetime +from monitorrent.plugins.clients import DownloadStatus + class DelugeCredentials(Base): __tablename__ = "deluge_credentials" @@ -150,5 +152,21 @@ def remove_torrent(self, torrent_hash): except: return False + def get_download_status(self, torrent_hash): + client = self._get_client() + lower_hash = torrent_hash.lower() + if not client: + return False + try: + client.connect() + result = client.call("core.get_torrents_status", + {'hash': lower_hash}, ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) + key, value = result.popitem() + return DownloadStatus(value[b'total_done'], value[b'total_size'], + value[b'download_payload_rate'], value[b'upload_payload_rate']) + except: + return False + register_plugin('client', 'deluge', DelugeClientPlugin()) diff --git a/monitorrent/plugins/clients/downloader.py b/monitorrent/plugins/clients/downloader.py index a0483dbf..fb204d5e 100644 --- a/monitorrent/plugins/clients/downloader.py +++ b/monitorrent/plugins/clients/downloader.py @@ -5,6 +5,7 @@ from sqlalchemy import Column, Integer, String from monitorrent.db import Base, DBSession from monitorrent.plugin_managers import register_plugin +from monitorrent.plugins.clients import DownloadStatus from monitorrent.utils.bittorrent_ex import Torrent import base64 @@ -108,4 +109,7 @@ def remove_torrent(self, torrent_hash): except OSError: return False + def get_download_status(self, torrent_hash): + return DownloadStatus(0, 0, 0, 0) + register_plugin('client', DownloaderPlugin.name, DownloaderPlugin()) diff --git a/monitorrent/plugins/clients/qbittorrent.py b/monitorrent/plugins/clients/qbittorrent.py index e485fc71..4a2f8850 100644 --- a/monitorrent/plugins/clients/qbittorrent.py +++ b/monitorrent/plugins/clients/qbittorrent.py @@ -17,6 +17,8 @@ from datetime import datetime import dateutil.parser +from monitorrent.plugins.clients import DownloadStatus + class QBittorrentCredentials(Base): __tablename__ = "qbittorrent_credentials" @@ -179,4 +181,17 @@ def remove_torrent(self, torrent_hash): except: return False + def get_download_status(self, torrent_hash): + parameters = self._get_params() + if not parameters: + return False + try: + torrent_hash = torrent_hash.lower() + response = parameters['session'].get(parameters['target'] + "query/propertiesGeneral/" + torrent_hash) + response.raise_for_status() + result = response.json() + return DownloadStatus(result['total_downloaded'], result['total_size'], result['dl_speed'], result['up_speed']) + except: + return False + register_plugin('client', 'qbittorrent', QBittorrentClientPlugin()) diff --git a/monitorrent/plugins/clients/transmission.py b/monitorrent/plugins/clients/transmission.py index e02f11d2..9e3d8108 100644 --- a/monitorrent/plugins/clients/transmission.py +++ b/monitorrent/plugins/clients/transmission.py @@ -6,6 +6,8 @@ from monitorrent.plugin_managers import register_plugin import base64 +from monitorrent.plugins.clients import DownloadStatus + class TransmissionCredentials(Base): __tablename__ = "transmission_credentials" @@ -131,4 +133,16 @@ def remove_torrent(self, torrent_hash): except transmissionrpc.TransmissionError: return False + def get_download_status(self, torrent_hash): + client = self.check_connection() + if not client: + return False + try: + torrent = client.get_torrent(torrent_hash.lower(), ['id', 'hashString', 'totalSize', 'downloadedEver', + 'rateDownload', 'rateUpload']) + return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload) + except transmissionrpc.TransmissionError: + return False + + register_plugin('client', 'transmission', TransmissionClientPlugin()) diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index 79e18ff8..f37925cc 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -11,6 +11,7 @@ from monitorrent.db import Base, DBSession from monitorrent.plugin_managers import register_plugin +from monitorrent.plugins.clients import DownloadStatus from monitorrent.utils.soup import get_soup @@ -146,4 +147,21 @@ def remove_torrent(self, torrent_hash): except: return False + def get_download_status(self, torrent_hash): + parameters = self._get_params() + if not parameters: + return False + + payload = {"list": '1', "token": parameters["token"]} + try: + torrents = parameters['session'].get(parameters['target'], + params=payload) + array = json.loads(torrents.text)['torrents'] + torrent = next(torrent for torrent in array if torrent[0] == torrent_hash) + if torrent: + return DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) + except: + return False + + register_plugin('client', 'utorrent', UTorrentClientPlugin()) diff --git a/monitorrent/rest/clients.py b/monitorrent/rest/clients.py index a02077ad..46045704 100644 --- a/monitorrent/rest/clients.py +++ b/monitorrent/rest/clients.py @@ -1,3 +1,4 @@ +import json from builtins import str from builtins import object import falcon @@ -44,6 +45,17 @@ def on_put(self, req, resp, client): resp.status = falcon.HTTP_NO_CONTENT +class TorrentStatus(object): + def __init__(self, clients_manager): + """ + :type claients_manager: ClientsManager + """ + self.clients_manager = clients_manager + + def on_get(self, req, resp, torrent_hash): + resp.json = self.clients_manager.get_download_status(torrent_hash).__dict__ + + # noinspection PyUnusedLocal class ClientCheck(object): def __init__(self, clients_manager): diff --git a/server.py b/server.py index e2a746fe..57b9b459 100644 --- a/server.py +++ b/server.py @@ -20,7 +20,7 @@ from monitorrent.rest.login import Login, Logout from monitorrent.rest.topics import TopicCollection, TopicParse, Topic, TopicResetStatus, TopicPauseState from monitorrent.rest.trackers import TrackerCollection, Tracker, TrackerCheck -from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault +from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus from monitorrent.rest.settings_authentication import SettingsAuthentication from monitorrent.rest.settings_password import SettingsPassword from monitorrent.rest.settings_execute import SettingsExecute @@ -68,6 +68,7 @@ def create_app(secret_key, token, tracker_manager, clients_manager, notifier_man app.add_route('/api/clients/{client}', Client(clients_manager)) app.add_route('/api/clients/{client}/check', ClientCheck(clients_manager)) app.add_route('/api/clients/{client}/default', ClientDefault(clients_manager)) + app.add_route('/api/clients/torrent/status/{torrent_hash}', TorrentStatus(clients_manager)) app.add_route('/api/notifiers', NotifierCollection(notifier_manager)) app.add_route('/api/notifiers/{notifier}', Notifier(notifier_manager)) app.add_route('/api/notifiers/{notifier}/check', NotifierCheck(notifier_manager)) diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found new file mode 100644 index 00000000..9849043d --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found @@ -0,0 +1,52 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
hb-E2dJ3TaHyN8RbQYY2Tc6JV4BhG6MfJzLqldAcAeH7Vbe0dXbaP8YTw1gAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=6c9mKGrUDoKyoFFeRbOu; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=6c9mKGrUDoKyoFFeRbOu] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=hb-E2dJ3TaHyN8RbQYY2Tc6JV4BhG6MfJzLqldAcAeH7Vbe0dXbaP8YTw1gAAAAA + response: + body: {string: '{"build":43388,"torrents": [ + + ], + + "label": [],"torrentc": "88476001" + + ,"rssfeeds": [] + + ,"rssfilters": [] + + } + + '} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['103'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success new file mode 100644 index 00000000..2d18a696 --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success @@ -0,0 +1,55 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
Xn2DQbsqtvnaRfDynL-ygq7jK3xQEQUWM_XjFUOjtmqnI32sx4q4EpwRw1gAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=fQkPP3URCwpgpAcclRFB; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=fQkPP3URCwpgpAcclRFB] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=Xn2DQbsqtvnaRfDynL-ygq7jK3xQEQUWM_XjFUOjtmqnI32sx4q4EpwRw1gAAAAA + response: + body: {string: '{"build":43388,"torrents": [ + + + ["44E416FCD3DBF967E292B1C1965306EA86FDB74D",233,"Kubo and the Two Strings + (2016) BDRip 720p.mkv",3821779548,1000,3821779548,0,0,11,10,-1,"",1,200,0,133,70853,-1,0,"","","Paused + 100.0 %","1",1489178139,1489178798,"","C:\\Users\\Dzmit\\Downloads",0,"4AD0784F"]], + + "label": [],"torrentc": "1272594106" + + ,"rssfeeds": [] + + ,"rssfilters": [] + + } + + '} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['362'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py index d209342d..a1900900 100644 --- a/tests/plugins/clients/test_deluge_plugin.py +++ b/tests/plugins/clients/test_deluge_plugin.py @@ -270,3 +270,55 @@ def test_get_download_dir_exception(self, deluge_client): assert plugin.get_download_dir() is None rpc_client.call.assert_called_once_with('core.get_config_value', 'download_location') + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_get_torrent_status_successfully(self, deluge_client): + torrent_hash = 'SomeRandomHashMockString' + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30, + b'total_size': 100, + b'download_payload_rate': 50000.0, + b'upload_payload_rate': 0.0}} + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + result = plugin.get_download_status(torrent_hash) + assert result.download_speed == 50000.0 + assert result.upload_speed == 0.0 + assert result.total_bytes == 100 + assert result.downloaded_bytes == 30 + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_when_no_settings_on_get_torrent_status(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + torrent_hash = 'SomeRandomHashMockString' + + plugin = DelugeClientPlugin() + + assert plugin.get_download_status(torrent_hash) is False + + rpc_client.call.assert_not_called() + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_get_torrent_status_on_error(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + torrent_hash = 'SomeRandomHashMockString' + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + assert plugin.get_download_status(torrent_hash) is False + + rpc_client.call.assert_called_once_with("core.get_torrents_status", + {'hash': torrent_hash.lower()}, ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) diff --git a/tests/plugins/clients/test_downloader.py b/tests/plugins/clients/test_downloader.py index 85624935..cfcca7ac 100644 --- a/tests/plugins/clients/test_downloader.py +++ b/tests/plugins/clients/test_downloader.py @@ -197,3 +197,11 @@ def test_remove_torrent_failed(self): remove.side_effect = OSError self.assertFalse(plugin.remove_torrent(torrent.info_hash)) self.assertTrue(os.path.exists(downloaded_filepath)) + + def test_download_status(self): + plugin = DownloaderPlugin() + status = plugin.get_download_status("torrent") + assert status.upload_speed == 0 + assert status.download_speed == 0 + assert status.total_bytes == 0 + assert status.downloaded_bytes == 0 diff --git a/tests/plugins/clients/test_qbittorrent.py b/tests/plugins/clients/test_qbittorrent.py index edab73ce..697a4e1a 100644 --- a/tests/plugins/clients/test_qbittorrent.py +++ b/tests/plugins/clients/test_qbittorrent.py @@ -243,3 +243,79 @@ def test_get_download_dir_error(self, mocker): plugin.set_settings(settings) assert plugin.get_download_dir() is None + + @Mocker() + def test_should_get_download_status_successfully(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + hash_string = 'hash' + + mocker.get(target + "query/propertiesGeneral/" + hash_string, + status_code=200, + text="{\"addition_date\":1487792728,\"comment\":\"\",\"completion_date\":-1,\"created_by\":\"\"," + "\"creation_date\":1487792646,\"dl_limit\":-1,\"dl_speed\":5990252,\"dl_speed_avg\":5851317," + "\"eta\":1025,\"last_seen\":1487792930,\"nb_connections\":16,\"nb_connections_limit\":100," + "\"peers\":2,\"peers_total\":15,\"piece_size\":8388608,\"pieces_have\":136," + "\"pieces_num\":871,\"reannounce\":2849," + "\"save_path\":\"C:\\\\Users\\\\DSilence\\\\Downloads\\\\test_dir\\\\\",\"seeding_time\":0," + "\"seeds\":14,\"seeds_total\":16,\"share_ratio\":0.015455593117895568,\"time_elapsed\":202," + "\"total_downloaded\":1187817372,\"total_downloaded_session\":1187817372," + "\"total_size\":7299496252,\"total_uploaded\":18358422,\"total_uploaded_session\":18358422," + "\"total_wasted\":110,\"up_limit\":-1,\"up_speed\":129319,\"up_speed_avg\":90435}") + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + result = plugin.get_download_status(hash_string) + assert result.upload_speed == 129319 + assert result.download_speed == 5990252 + assert result.downloaded_bytes == 1187817372 + assert result.total_bytes == 7299496252 + + @Mocker() + def test_should_fail_download_status_when_not_logged_in(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Fails.") + + hash_string = 'hash' + + mocker.get(target + "query/propertiesGeneral/" + hash_string, + status_code=200, + text="{\"addition_date\":1487792728,\"comment\":\"\",\"completion_date\":-1,\"created_by\":\"\"," + "\"creation_date\":1487792646,\"dl_limit\":-1,\"dl_speed\":5990252,\"dl_speed_avg\":5851317," + "\"eta\":1025,\"last_seen\":1487792930,\"nb_connections\":16,\"nb_connections_limit\":100," + "\"peers\":2,\"peers_total\":15,\"piece_size\":8388608,\"pieces_have\":136," + "\"pieces_num\":871,\"reannounce\":2849," + "\"save_path\":\"C:\\\\Users\\\\DSilence\\\\Downloads\\\\test_dir\\\\\",\"seeding_time\":0," + "\"seeds\":14,\"seeds_total\":16,\"share_ratio\":0.015455593117895568,\"time_elapsed\":202," + "\"total_downloaded\":1187817372,\"total_downloaded_session\":1187817372," + "\"total_size\":7299496252,\"total_uploaded\":18358422,\"total_uploaded_session\":18358422," + "\"total_wasted\":110,\"up_limit\":-1,\"up_speed\":129319,\"up_speed_avg\":90435}") + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + assert plugin.get_download_status(hash_string) is False + + @Mocker() + def test_should_fail_when_download_status_wasnt_retrieved(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + error = {'error': 500} + hash_string = 'hash' + mocker.get(target + "query/propertiesGeneral/" + hash_string, status_code=500, text=json.dumps(error)) + + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + assert plugin.get_download_status(hash_string) is False diff --git a/tests/plugins/clients/test_transmission_plugin.py b/tests/plugins/clients/test_transmission_plugin.py index 227d5b38..60d09086 100644 --- a/tests/plugins/clients/test_transmission_plugin.py +++ b/tests/plugins/clients/test_transmission_plugin.py @@ -130,7 +130,8 @@ def test_add_torrent_with_settings(self, transmission_client): torrent = b'!torrent.content' self.assertTrue(plugin.add_torrent(torrent, TopicSettings('/path/to/download/dir'))) - rpc_client.add_torrent.assert_called_once_with(base64.b64encode(torrent).decode('utf-8'), download_dir='/path/to/download/dir') + rpc_client.add_torrent.assert_called_once_with(base64.b64encode(torrent).decode('utf-8'), + download_dir='/path/to/download/dir') @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') def test_add_torrent_without_credentials(self, transmission_client): @@ -225,3 +226,42 @@ def test_get_download_dir_exception(self, transmission_client): assert plugin.get_download_dir() is None rpc_client.get_session.assert_called_once() + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_get_status(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrent.return_value = namedtuple('Torrent', + ('rateDownload', 'totalSize', 'downloadedEver', 'rateUpload') + )(5000, 10000, 6000, 20) + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + status = plugin.get_download_status('4e2597302ad6b4d7a545c8ec02621ac232316b96') + assert status.downloaded_bytes == 6000 + assert status.download_speed == 5000 + assert status.total_bytes == 10000 + assert status.upload_speed == 20 + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_when_no_settings_for_get_torrent(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + assert plugin.get_download_status('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False + rpc_client.get_torrent.assert_not_called() + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_when_get_torrent_fails(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + assert plugin.get_download_status('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False + + rpc_client.get_torrent.assert_called_once() diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py index 314ae9ad..bc94ca33 100644 --- a/tests/plugins/clients/test_utorrent_plugin.py +++ b/tests/plugins/clients/test_utorrent_plugin.py @@ -161,3 +161,34 @@ def test_remove_torrent_success(self, get_mock): torrent = b'torrent' self.assertTrue(plugin.remove_torrent(torrent)) + + @use_vcr + def test_get_download_status_success(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + torrent = "44E416FCD3DBF967E292B1C1965306EA86FDB74D" + result = plugin.get_download_status(torrent) + + assert result.upload_speed == 11 + assert result.download_speed == 10 + assert result.downloaded_bytes == 3821779548 + assert result.total_bytes == 3821779548 + + @patch('requests.Session.get') + def test_get_download_status_bad_settings(self, get_mock): + plugin = UTorrentClientPlugin() + torrent = 'torrent' + self.assertFalse(plugin.get_download_status(torrent)) + + @use_vcr + def test_get_download_status_not_found(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + torrent = "torrent" + assert plugin.get_download_status(torrent) is False diff --git a/tests/rest/test_api_clients.py b/tests/rest/test_api_clients.py index 574fa15a..c1f78d61 100644 --- a/tests/rest/test_api_clients.py +++ b/tests/rest/test_api_clients.py @@ -3,8 +3,10 @@ import falcon from mock import MagicMock from ddt import ddt, data + +from monitorrent.plugins.clients import DownloadStatus from tests import RestTestBase -from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault +from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus from monitorrent.plugin_managers import ClientsManager @@ -107,6 +109,27 @@ def test_not_found_update_settings(self): self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) +@ddt +class TorrentStatusTest(RestTestBase): + def test_get_download_status(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status = MagicMock(return_value=DownloadStatus(1, 2, 3, 4)) + + client = TorrentStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/torrent/status/{torrent_hash}', client) + + body = self.simulate_request('/api/clients/torrent/status/{0}'.format("hash"), decode="utf-8") + self.assertEqual(self.srmock.status, falcon.HTTP_OK) + self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type']) + + result = json.loads(body) + assert result['downloaded_bytes'] == 1 + assert result['total_bytes'] == 2 + assert result['download_speed'] == 3 + assert result['upload_speed'] == 4 + + @ddt class CheckClientTest(RestTestBase): @data(True, False) @@ -215,4 +238,3 @@ def test_set_default_not_found(self): self.simulate_request('/api/clients/{0}/default'.format('random.org'), method='PUT') self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) - diff --git a/tests/test_plugin_managers_clients_manager.py b/tests/test_plugin_managers_clients_manager.py index b73e6160..8ec28873 100644 --- a/tests/test_plugin_managers_clients_manager.py +++ b/tests/test_plugin_managers_clients_manager.py @@ -1,5 +1,7 @@ from ddt import ddt, data from mock import Mock, MagicMock, patch + +from monitorrent.plugins.clients import DownloadStatus from tests import TestCase, DbTestCase from monitorrent.plugin_managers import ClientsManager, DbClientsManager from monitorrent.settings_manager import SettingsManager @@ -173,6 +175,17 @@ def test_remove_torrent_false(self): remove_torrent_mock1.assert_called_once_with(torrent_hash) remove_torrent_mock2.assert_not_called() + def test_get_download_status_success(self): + result = DownloadStatus(1, 2, 3, 4) + get_download_status1 = MagicMock(return_value=result) + get_download_status2 = MagicMock(return_value=False) + + torrent_hash = 'hash' + self.client1.get_download_status = get_download_status1 + self.client2.get_download_status = get_download_status2 + actual_result = self.clients_manager.get_download_status(torrent_hash) + assert actual_result == result + def test_get_plugins(self): clients = self.clients_manager.clients get_plugins_mock = Mock(return_value=clients) @@ -196,6 +209,7 @@ def test_empty_clients(self): self.assertFalse(clients_amanger.add_torrent('!torrent', None)) self.assertFalse(clients_amanger.find_torrent('hash')) self.assertFalse(clients_amanger.remove_torrent('hash')) + self.assertFalse(clients_amanger.get_download_status('hash')) def test_unknow_client(self): with self.assertRaises(KeyError): From 6924a115694dba8cbbee65e27a0303c8a42e0306 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sat, 11 Mar 2017 21:11:57 +0300 Subject: [PATCH 02/15] fix(plugins/clients/deluge): fixed deluge issues #200 --- monitorrent/plugins/clients/deluge.py | 6 +++--- tests/plugins/clients/test_deluge_plugin.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index df067b31..af148448 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -97,7 +97,7 @@ def get_download_dir(self): return None try: client.connect() - return client.call('core.get_config_value', 'download_location').decode('utf-8') + return client.call('core.get_config_value', 'move_completed_path').decode('utf-8') except: return None @@ -115,8 +115,8 @@ def find_torrent(self, torrent_hash): return False # time_added return time in local timezone, so lets convert it to UTC return { - "name": torrent['name'], - "date_added": datetime.utcfromtimestamp(torrent['time_added']).replace(tzinfo=pytz.utc) + "name": torrent[b'name'].decode('utf-8'), + "date_added": datetime.utcfromtimestamp(torrent[b'time_added']).replace(tzinfo=pytz.utc) } def add_torrent(self, torrent, torrent_settings): diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py index a1900900..dd8a8bfe 100644 --- a/tests/plugins/clients/test_deluge_plugin.py +++ b/tests/plugins/clients/test_deluge_plugin.py @@ -78,7 +78,7 @@ def test_find_torrent(self, deluge_client): plugin.set_settings(settings) date_added = datetime(2015, 10, 9, 12, 3, 55, tzinfo=pytz.reference.LocalTimezone()) - rpc_client.call.return_value = {'name': 'Torrent 1', 'time_added': time.mktime(date_added.timetuple())} + rpc_client.call.return_value = {b'name': b'Torrent 1', b'time_added': time.mktime(date_added.timetuple())} torrent_hash = 'SomeRandomHashMockString' torrent = plugin.find_torrent(torrent_hash) @@ -255,7 +255,7 @@ def test_get_download_dir_success(self, deluge_client): assert plugin.get_download_dir() == u'/mnt/media/torrents/complete' - rpc_client.call.assert_called_once_with('core.get_config_value', 'download_location') + rpc_client.call.assert_called_once_with('core.get_config_value', 'move_completed_path') @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') def test_get_download_dir_exception(self, deluge_client): @@ -269,7 +269,7 @@ def test_get_download_dir_exception(self, deluge_client): assert plugin.get_download_dir() is None - rpc_client.call.assert_called_once_with('core.get_config_value', 'download_location') + rpc_client.call.assert_called_once_with('core.get_config_value', 'move_completed_path') @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') def test_should_get_torrent_status_successfully(self, deluge_client): From bb18b29f410537fafaa49d39cceb50468b4dd690 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Wed, 15 Mar 2017 23:45:17 +0300 Subject: [PATCH 03/15] feat(plugins/clients): code comments changes --- monitorrent/plugin_managers.py | 14 +++- monitorrent/plugins/clients/__init__.py | 3 +- monitorrent/plugins/clients/deluge.py | 21 ++++- monitorrent/plugins/clients/downloader.py | 5 +- monitorrent/plugins/clients/qbittorrent.py | 18 +++- monitorrent/plugins/clients/transmission.py | 18 +++- monitorrent/plugins/clients/utorrent.py | 19 ++++- monitorrent/rest/clients.py | 28 ++++++- server.py | 4 +- ...test_get_download_status_by_hash_not_found | 54 ++++++++++++ ...t.test_get_download_status_by_hash_success | 54 ++++++++++++ ...ginTest.test_get_download_status_not_found | 24 ++---- ...luginTest.test_get_download_status_success | 17 ++-- tests/plugins/clients/test_deluge_plugin.py | 65 +++++++++++++-- tests/plugins/clients/test_downloader.py | 12 ++- tests/plugins/clients/test_qbittorrent.py | 82 +++++++++++++++++-- .../clients/test_transmission_plugin.py | 55 +++++++++++-- tests/plugins/clients/test_utorrent_plugin.py | 53 +++++++++--- tests/rest/test_api_clients.py | 39 ++++++++- tests/test_plugin_managers_clients_manager.py | 23 ++++-- 20 files changed, 522 insertions(+), 86 deletions(-) create mode 100644 tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found create mode 100644 tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success diff --git a/monitorrent/plugin_managers.py b/monitorrent/plugin_managers.py index 0d5bb453..38ae17c6 100644 --- a/monitorrent/plugin_managers.py +++ b/monitorrent/plugin_managers.py @@ -204,11 +204,19 @@ def find_torrent(self, torrent_hash): result = self.default_client.find_torrent(torrent_hash) return result or False - def get_download_status(self, torrent_hash): + def get_download_status(self, client_name): + client = self.get_client(client_name) + result = client.get_download_status() + return result or False + + def get_download_status_by_id(self, torrent_id): + """ + + :type torrent_id: str + """ if self.default_client is None: return False - result = self.default_client.get_download_status(torrent_hash) - return result or False + return self.default_client.get_download_status_by_hash(torrent_id) def add_torrent(self, torrent, topic_settings): """ diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py index c17037ab..3441d9ce 100644 --- a/monitorrent/plugins/clients/__init__.py +++ b/monitorrent/plugins/clients/__init__.py @@ -1,14 +1,13 @@ from monitorrent.plugins.trackers import Topic -class DownloadStatus(object): +class DownloadStatus(dict): def __init__(self, downloaded_bytes, total_bytes, download_speed, upload_speed): self.downloaded_bytes = downloaded_bytes self.total_bytes = total_bytes self.download_speed = download_speed self.upload_speed = upload_speed - class TopicSettings(object): download_dir = None diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index af148448..6a32f28d 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -152,7 +152,24 @@ def remove_torrent(self, torrent_hash): except: return False - def get_download_status(self, torrent_hash): + def get_download_status(self): + client = self._get_client() + if not client: + return False + try: + client.connect() + result = client.call("core.get_torrents_status", + {}, ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) + statuses = {} + for key, value in result.items(): + statuses[key] = DownloadStatus(value[b'total_done'], value[b'total_size'], + value[b'download_payload_rate'], value[b'upload_payload_rate']) + return statuses + except: + return False + + def get_download_status_by_hash(self, torrent_hash): client = self._get_client() lower_hash = torrent_hash.lower() if not client: @@ -161,7 +178,7 @@ def get_download_status(self, torrent_hash): client.connect() result = client.call("core.get_torrents_status", {'hash': lower_hash}, ['total_done', 'total_size', 'download_payload_rate', - 'upload_payload_rate', 'state', 'progress']) + 'upload_payload_rate', 'state', 'progress']) key, value = result.popitem() return DownloadStatus(value[b'total_done'], value[b'total_size'], value[b'download_payload_rate'], value[b'upload_payload_rate']) diff --git a/monitorrent/plugins/clients/downloader.py b/monitorrent/plugins/clients/downloader.py index fb204d5e..69d26dde 100644 --- a/monitorrent/plugins/clients/downloader.py +++ b/monitorrent/plugins/clients/downloader.py @@ -109,7 +109,10 @@ def remove_torrent(self, torrent_hash): except OSError: return False - def get_download_status(self, torrent_hash): + def get_download_status(self): + return {"": DownloadStatus(0, 0, 0, 0)} + + def get_download_status_by_hash(self, torrent_hash): return DownloadStatus(0, 0, 0, 0) register_plugin('client', DownloaderPlugin.name, DownloaderPlugin()) diff --git a/monitorrent/plugins/clients/qbittorrent.py b/monitorrent/plugins/clients/qbittorrent.py index 4a2f8850..46285326 100644 --- a/monitorrent/plugins/clients/qbittorrent.py +++ b/monitorrent/plugins/clients/qbittorrent.py @@ -181,7 +181,23 @@ def remove_torrent(self, torrent_hash): except: return False - def get_download_status(self, torrent_hash): + def get_download_status(self): + parameters = self._get_params() + if not parameters: + return False + try: + response = parameters['session'].get(parameters['target'] + "query/torrents/") + response.raise_for_status() + result = response.json() + torrents = {} + for torrent in result: + torrents[torrent['hash']] = DownloadStatus(torrent['progress'] * torrent['size'], torrent['size'], torrent['dlspeed'], + torrent['upspeed']) + return torrents + except: + return False + + def get_download_status_by_hash(self, torrent_hash): parameters = self._get_params() if not parameters: return False diff --git a/monitorrent/plugins/clients/transmission.py b/monitorrent/plugins/clients/transmission.py index 9e3d8108..d64b5acb 100644 --- a/monitorrent/plugins/clients/transmission.py +++ b/monitorrent/plugins/clients/transmission.py @@ -133,7 +133,23 @@ def remove_torrent(self, torrent_hash): except transmissionrpc.TransmissionError: return False - def get_download_status(self, torrent_hash): + def get_download_status(self): + client = self.check_connection() + if not client: + return False + try: + torrents = client.get_torrents(None, ['id', 'hashString', 'totalSize', 'downloadedEver', + 'rateDownload', 'rateUpload']) + result = {} + for torrent in torrents: + result[torrent.hashString] = DownloadStatus(torrent.downloadedEver, torrent.totalSize, + torrent.rateDownload, + torrent.rateUpload) + return result + except transmissionrpc.TransmissionError: + return False + + def get_download_status_by_hash(self, torrent_hash): client = self.check_connection() if not client: return False diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index f37925cc..d76b7633 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -147,7 +147,24 @@ def remove_torrent(self, torrent_hash): except: return False - def get_download_status(self, torrent_hash): + def get_download_status(self): + parameters = self._get_params() + if not parameters: + return False + + payload = {"list": '1', "token": parameters["token"]} + try: + torrents = parameters['session'].get(parameters['target'], + params=payload) + array = json.loads(torrents.text)['torrents'] + result = {} + for torrent in array: + result[torrent[0]] = DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) + return result + except: + return False + + def get_download_status_by_hash(self, torrent_hash): parameters = self._get_params() if not parameters: return False diff --git a/monitorrent/rest/clients.py b/monitorrent/rest/clients.py index 46045704..e23a0ad4 100644 --- a/monitorrent/rest/clients.py +++ b/monitorrent/rest/clients.py @@ -48,12 +48,16 @@ def on_put(self, req, resp, client): class TorrentStatus(object): def __init__(self, clients_manager): """ - :type claients_manager: ClientsManager + :type clients_manager: ClientsManager """ self.clients_manager = clients_manager def on_get(self, req, resp, torrent_hash): - resp.json = self.clients_manager.get_download_status(torrent_hash).__dict__ + """ + + :type torrent_hash: str + """ + resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ # noinspection PyUnusedLocal @@ -120,3 +124,23 @@ def on_put(self, req, resp, client): except KeyError as e: raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) resp.status = falcon.HTTP_NO_CONTENT + + +class ClientStatus(object): + def __init__(self, clients_manager): + """ + :type clients_manager: ClientsManager + """ + self.clients_manager = clients_manager + + def on_get(self, req, resp, client): + try: + result = self.clients_manager.get_download_status(client) + result_dict = {} + for key, value in result.items(): + result_dict[key] = value.__dict__ + resp.json = result_dict + except KeyError as e: + raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + resp.status = falcon.HTTP_OK + diff --git a/server.py b/server.py index e6b55c7e..83849315 100644 --- a/server.py +++ b/server.py @@ -20,7 +20,8 @@ from monitorrent.rest.login import Login, Logout from monitorrent.rest.topics import TopicCollection, TopicParse, Topic, TopicResetStatus, TopicPauseState from monitorrent.rest.trackers import TrackerCollection, Tracker, TrackerCheck -from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus +from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus, \ + ClientStatus from monitorrent.rest.settings_authentication import SettingsAuthentication from monitorrent.rest.settings_password import SettingsPassword from monitorrent.rest.settings_execute import SettingsExecute @@ -68,6 +69,7 @@ def create_app(secret_key, token, tracker_manager, clients_manager, notifier_man app.add_route('/api/clients/{client}', Client(clients_manager)) app.add_route('/api/clients/{client}/check', ClientCheck(clients_manager)) app.add_route('/api/clients/{client}/default', ClientDefault(clients_manager)) + app.add_route('/api/clients/{client}/status', ClientStatus(clients_manager)) app.add_route('/api/clients/torrent/status/{torrent_hash}', TorrentStatus(clients_manager)) app.add_route('/api/notifiers', NotifierCollection(notifier_manager)) app.add_route('/api/notifiers/{notifier}', Notifier(notifier_manager)) diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found new file mode 100644 index 00000000..2fc8e205 --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_not_found @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=ueUrdYTfO6krxw1B8KYu; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=ueUrdYTfO6krxw1B8KYu] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA + response: + body: {string: '{"build":43388,"torrents": [ + + + ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1250541568,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4369250876,"","","Downloading + 22.2 %","1",1489828545,0,"","C:\\Users\\DSilence\\Downloads",0,"75BD3F66"]], + + "label": [],"torrentc": "1089974307" + + ,"rssfeeds": [] + + ,"rssfilters": [] + + } + + '} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['392'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success new file mode 100644 index 00000000..e1b046a7 --- /dev/null +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_by_hash_success @@ -0,0 +1,54 @@ +interactions: +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/token.html + response: + body: {string: '
3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
'} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['121'] + Content-Type: [text/html] + Set-Cookie: [GUID=5NB1vxis1FNc70BFwRXi; path=/] + status: {code: 200, message: OK} +- request: + body: null + headers: + Accept: ['*/*'] + Accept-Encoding: ['gzip, deflate'] + Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] + Connection: [keep-alive] + Cookie: [GUID=5NB1vxis1FNc70BFwRXi] + User-Agent: [python-requests/2.13.0] + method: GET + uri: http://localhost:8080/gui/?list=1&token=3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA + response: + body: {string: '{"build":43388,"torrents": [ + + + ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1251196928,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4368595516,"","","Downloading + 22.2 %","1",1489828545,0,"","C:\\Users\\Test\\Downloads",0,"75BD3F66"]], + + "label": [],"torrentc": "675099747" + + ,"rssfeeds": [] + + ,"rssfilters": [] + + } + + '} + headers: + Cache-Control: [no-cache] + Connection: [keep-alive] + Content-Length: ['391'] + Content-Type: [text/plain] + status: {code: 200, message: OK} +version: 1 diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found index 9849043d..dc8b787c 100644 --- a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_not_found @@ -10,13 +10,13 @@ interactions: method: GET uri: http://localhost:8080/gui/token.html response: - body: {string: '
hb-E2dJ3TaHyN8RbQYY2Tc6JV4BhG6MfJzLqldAcAeH7Vbe0dXbaP8YTw1gAAAAA
'} + body: {string: '
iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA
'} headers: Cache-Control: [no-cache] Connection: [keep-alive] Content-Length: ['121'] Content-Type: [text/html] - Set-Cookie: [GUID=6c9mKGrUDoKyoFFeRbOu; path=/] + Set-Cookie: [GUID=ueUrdYTfO6krxw1B8KYu; path=/] status: {code: 200, message: OK} - request: body: null @@ -25,28 +25,16 @@ interactions: Accept-Encoding: ['gzip, deflate'] Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] Connection: [keep-alive] - Cookie: [GUID=6c9mKGrUDoKyoFFeRbOu] + Cookie: [GUID=ueUrdYTfO6krxw1B8KYu] User-Agent: [python-requests/2.13.0] method: GET - uri: http://localhost:8080/gui/?list=1&token=hb-E2dJ3TaHyN8RbQYY2Tc6JV4BhG6MfJzLqldAcAeH7Vbe0dXbaP8YTw1gAAAAA + uri: http://localhost:8080/gui/?list=1&token=iCEj-xbAgTOaaeiKOsvzyoHIcwP93kS_MHyHDPsjmjJSDW0tbGVNbd77zFgAAAAA response: - body: {string: '{"build":43388,"torrents": [ - - ], - - "label": [],"torrentc": "88476001" - - ,"rssfeeds": [] - - ,"rssfilters": [] - - } - - '} + body: {string: ''} headers: Cache-Control: [no-cache] Connection: [keep-alive] - Content-Length: ['103'] + Content-Length: ['392'] Content-Type: [text/plain] status: {code: 200, message: OK} version: 1 diff --git a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success index 2d18a696..e1b046a7 100644 --- a/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success +++ b/tests/cassettes/test_utorrent_plugin.UTorrentPluginTest.test_get_download_status_success @@ -10,13 +10,13 @@ interactions: method: GET uri: http://localhost:8080/gui/token.html response: - body: {string: '
Xn2DQbsqtvnaRfDynL-ygq7jK3xQEQUWM_XjFUOjtmqnI32sx4q4EpwRw1gAAAAA
'} + body: {string: '
3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA
'} headers: Cache-Control: [no-cache] Connection: [keep-alive] Content-Length: ['121'] Content-Type: [text/html] - Set-Cookie: [GUID=fQkPP3URCwpgpAcclRFB; path=/] + Set-Cookie: [GUID=5NB1vxis1FNc70BFwRXi; path=/] status: {code: 200, message: OK} - request: body: null @@ -25,19 +25,18 @@ interactions: Accept-Encoding: ['gzip, deflate'] Authorization: [Basic YWRtaW46cGFzc3dvcmQ=] Connection: [keep-alive] - Cookie: [GUID=fQkPP3URCwpgpAcclRFB] + Cookie: [GUID=5NB1vxis1FNc70BFwRXi] User-Agent: [python-requests/2.13.0] method: GET - uri: http://localhost:8080/gui/?list=1&token=Xn2DQbsqtvnaRfDynL-ygq7jK3xQEQUWM_XjFUOjtmqnI32sx4q4EpwRw1gAAAAA + uri: http://localhost:8080/gui/?list=1&token=3N-t0vKNCuF_9FhJ4qcCCcumfFp-0Ymx1m1K-X3zF2jEhR8RvBPDCN_7zFgAAAAA response: body: {string: '{"build":43388,"torrents": [ - ["44E416FCD3DBF967E292B1C1965306EA86FDB74D",233,"Kubo and the Two Strings - (2016) BDRip 720p.mkv",3821779548,1000,3821779548,0,0,11,10,-1,"",1,200,0,133,70853,-1,0,"","","Paused - 100.0 %","1",1489178139,1489178798,"","C:\\Users\\Dzmit\\Downloads",0,"4AD0784F"]], + ["3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE",201,"Sherlock.S04E01.The.Six.Thatchers.1080i.HDTV.Rus.HDCLUB.ts",5619792444,222,1251196928,0,0,6277,3106179,1005,"",0,218,36,62,2373454,1,4368595516,"","","Downloading + 22.2 %","1",1489828545,0,"","C:\\Users\\Test\\Downloads",0,"75BD3F66"]], - "label": [],"torrentc": "1272594106" + "label": [],"torrentc": "675099747" ,"rssfeeds": [] @@ -49,7 +48,7 @@ interactions: headers: Cache-Control: [no-cache] Connection: [keep-alive] - Content-Length: ['362'] + Content-Length: ['391'] Content-Type: [text/plain] status: {code: 200, message: OK} version: 1 diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py index dd8a8bfe..854114a0 100644 --- a/tests/plugins/clients/test_deluge_plugin.py +++ b/tests/plugins/clients/test_deluge_plugin.py @@ -272,7 +272,7 @@ def test_get_download_dir_exception(self, deluge_client): rpc_client.call.assert_called_once_with('core.get_config_value', 'move_completed_path') @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') - def test_should_get_torrent_status_successfully(self, deluge_client): + def test_should_get_torrent_status_by_hash_successfully(self, deluge_client): torrent_hash = 'SomeRandomHashMockString' rpc_client = deluge_client.return_value rpc_client.connected = True @@ -285,14 +285,14 @@ def test_should_get_torrent_status_successfully(self, deluge_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - result = plugin.get_download_status(torrent_hash) + result = plugin.get_download_status_by_hash(torrent_hash) assert result.download_speed == 50000.0 assert result.upload_speed == 0.0 assert result.total_bytes == 100 assert result.downloaded_bytes == 30 @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') - def test_should_fail_when_no_settings_on_get_torrent_status(self, deluge_client): + def test_should_fail_when_no_settings_on_get_torrent_status_by_hash(self, deluge_client): rpc_client = deluge_client.return_value rpc_client.connected = True rpc_client.call.side_effect = Exception @@ -301,12 +301,12 @@ def test_should_fail_when_no_settings_on_get_torrent_status(self, deluge_client) plugin = DelugeClientPlugin() - assert plugin.get_download_status(torrent_hash) is False + assert plugin.get_download_status_by_hash(torrent_hash) is False rpc_client.call.assert_not_called() @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') - def test_should_fail_get_torrent_status_on_error(self, deluge_client): + def test_should_fail_get_torrent_status_by_hash_on_error(self, deluge_client): rpc_client = deluge_client.return_value rpc_client.connected = True rpc_client.call.side_effect = Exception @@ -317,8 +317,57 @@ def test_should_fail_get_torrent_status_on_error(self, deluge_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_status(torrent_hash) is False + assert plugin.get_download_status_by_hash(torrent_hash) is False + + rpc_client.call.assert_called_once_with("core.get_torrents_status", + {'hash': torrent_hash.lower()}, + ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_get_torrent_statuses_successfully(self, deluge_client): + torrent_hash = 'SomeRandomHashMockString' + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30, + b'total_size': 100, + b'download_payload_rate': 50000.0, + b'upload_payload_rate': 0.0}} + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + result = plugin.get_download_status().popitem()[1] + assert result.download_speed == 50000.0 + assert result.upload_speed == 0.0 + assert result.total_bytes == 100 + assert result.downloaded_bytes == 30 + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_when_no_settings_on_get_torrent_statuses(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + plugin = DelugeClientPlugin() + + assert plugin.get_download_status() is False + + rpc_client.call.assert_not_called() + + @patch('monitorrent.plugins.clients.deluge.DelugeRPCClient') + def test_should_fail_get_torrent_status_on_error(self, deluge_client): + rpc_client = deluge_client.return_value + rpc_client.connected = True + rpc_client.call.side_effect = Exception + + plugin = DelugeClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + assert plugin.get_download_status() is False rpc_client.call.assert_called_once_with("core.get_torrents_status", - {'hash': torrent_hash.lower()}, ['total_done', 'total_size', 'download_payload_rate', - 'upload_payload_rate', 'state', 'progress']) + {}, ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) diff --git a/tests/plugins/clients/test_downloader.py b/tests/plugins/clients/test_downloader.py index cfcca7ac..7005fce4 100644 --- a/tests/plugins/clients/test_downloader.py +++ b/tests/plugins/clients/test_downloader.py @@ -70,7 +70,7 @@ def test_find_torrent(self): find_torrent = plugin.find_torrent(torrent.info_hash) self.assertNotEqual(False, find_torrent) - date_added = datetime.fromtimestamp(os.path.getctime(downloaded_filepath))\ + date_added = datetime.fromtimestamp(os.path.getctime(downloaded_filepath)) \ .replace(tzinfo=reference.LocalTimezone()).astimezone(utc) expected = {'name': torrent_filename, 'date_added': date_added} self.assertEqual(expected, find_torrent) @@ -198,9 +198,17 @@ def test_remove_torrent_failed(self): self.assertFalse(plugin.remove_torrent(torrent.info_hash)) self.assertTrue(os.path.exists(downloaded_filepath)) + def test_download_status_by_hash(self): + plugin = DownloaderPlugin() + status = plugin.get_download_status_by_hash("torrent") + assert status.upload_speed == 0 + assert status.download_speed == 0 + assert status.total_bytes == 0 + assert status.downloaded_bytes == 0 + def test_download_status(self): plugin = DownloaderPlugin() - status = plugin.get_download_status("torrent") + status = plugin.get_download_status().popitem()[1] assert status.upload_speed == 0 assert status.download_speed == 0 assert status.total_bytes == 0 diff --git a/tests/plugins/clients/test_qbittorrent.py b/tests/plugins/clients/test_qbittorrent.py index 697a4e1a..edf34770 100644 --- a/tests/plugins/clients/test_qbittorrent.py +++ b/tests/plugins/clients/test_qbittorrent.py @@ -245,7 +245,7 @@ def test_get_download_dir_error(self, mocker): assert plugin.get_download_dir() is None @Mocker() - def test_should_get_download_status_successfully(self, mocker): + def test_should_get_download_status_by_hash_successfully(self, mocker): target = "{0}:{1}/".format(self.real_host, self.real_port) mocker.post(target + "login", text="Ok.") @@ -269,14 +269,14 @@ def test_should_get_download_status_successfully(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - result = plugin.get_download_status(hash_string) + result = plugin.get_download_status_by_hash(hash_string) assert result.upload_speed == 129319 assert result.download_speed == 5990252 assert result.downloaded_bytes == 1187817372 assert result.total_bytes == 7299496252 @Mocker() - def test_should_fail_download_status_when_not_logged_in(self, mocker): + def test_should_fail_download_status_by_hash_when_not_logged_in(self, mocker): target = "{0}:{1}/".format(self.real_host, self.real_port) mocker.post(target + "login", text="Fails.") @@ -290,7 +290,7 @@ def test_should_fail_download_status_when_not_logged_in(self, mocker): "\"eta\":1025,\"last_seen\":1487792930,\"nb_connections\":16,\"nb_connections_limit\":100," "\"peers\":2,\"peers_total\":15,\"piece_size\":8388608,\"pieces_have\":136," "\"pieces_num\":871,\"reannounce\":2849," - "\"save_path\":\"C:\\\\Users\\\\DSilence\\\\Downloads\\\\test_dir\\\\\",\"seeding_time\":0," + "\"save_path\":\"C:\\\\Users\\\\sad\\\\tmp\\\\test_dir\\\\\",\"seeding_time\":0," "\"seeds\":14,\"seeds_total\":16,\"share_ratio\":0.015455593117895568,\"time_elapsed\":202," "\"total_downloaded\":1187817372,\"total_downloaded_session\":1187817372," "\"total_size\":7299496252,\"total_uploaded\":18358422,\"total_uploaded_session\":18358422," @@ -300,10 +300,10 @@ def test_should_fail_download_status_when_not_logged_in(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - assert plugin.get_download_status(hash_string) is False + assert plugin.get_download_status_by_hash(hash_string) is False @Mocker() - def test_should_fail_when_download_status_wasnt_retrieved(self, mocker): + def test_should_fail_when_download_status_by_hash_wasnt_retrieved(self, mocker): target = "{0}:{1}/".format(self.real_host, self.real_port) mocker.post(target + "login", text="Ok.") @@ -318,4 +318,72 @@ def test_should_fail_when_download_status_wasnt_retrieved(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - assert plugin.get_download_status(hash_string) is False + assert plugin.get_download_status_by_hash(hash_string) is False + + @Mocker() + def test_should_get_download_status_successfully(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + mocker.get(target + "query/torrents/", + status_code = 200, + text='[{"added_on":1489778322,"category":"","completion_on":4294967295,"dlspeed":5242223,' + '"eta":8640000,"f_l_piece_prio":false,"force_start":false,' + '"hash":"1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7",' + '"name":"en_windows_10_pro_10240_x64_dvd","num_complete":28,"num_incomplete":2,' + '"num_leechs":0,"num_seeds":0,"priority":1,"progress":0.20,"ratio":0,' + '"save_path":"C:\\\\Users\\\\DSilence\\\\Downloads\\\\","seq_dl":false,"size":3992293376,' + '"state":"pausedDL","super_seeding":false,"upspeed":0}]') + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + expected_key = '1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7' + status = plugin.get_download_status() + assert expected_key in status + result = status[expected_key] + assert result.upload_speed == 0 + assert result.download_speed == 5242223 + assert result.downloaded_bytes == 798458675.2 + assert result.total_bytes == 3992293376 + + @Mocker() + def test_should_fail_download_status_when_not_logged_in(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Fails.") + + mocker.get(target + "query/torrents/", + status_code=200, + text='[{"added_on":1489778322,"category":"","completion_on":4294967295,"dlspeed":5242223,' + '"eta":8640000,"f_l_piece_prio":false,"force_start":false,' + '"hash":"1bc6e893df6ee2d5b84aa41bb5cf4e189112a8a7",' + '"name":"en_windows_10_pro_10240_x64_dvd","num_complete":28,"num_incomplete":2,' + '"num_leechs":0,"num_seeds":0,"priority":1,"progress":0.20,"ratio":0,' + '"save_path":"C:\\\\Users\\\\DSilence\\\\Downloads\\\\","seq_dl":false,"size":3992293376,' + '"state":"pausedDL","super_seeding":false,"upspeed":0}]') + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + assert plugin.get_download_status() is False + + @Mocker() + def test_should_fail_when_download_status_wasnt_retrieved(self, mocker): + target = "{0}:{1}/".format(self.real_host, self.real_port) + + mocker.post(target + "login", text="Ok.") + + error = {'error': 500} + mocker.get(target + "query/torrents/", status_code=500, text=json.dumps(error)) + + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + + plugin = QBittorrentClientPlugin() + plugin.set_settings(settings) + + assert plugin.get_download_status() is False diff --git a/tests/plugins/clients/test_transmission_plugin.py b/tests/plugins/clients/test_transmission_plugin.py index 60d09086..13b52c05 100644 --- a/tests/plugins/clients/test_transmission_plugin.py +++ b/tests/plugins/clients/test_transmission_plugin.py @@ -228,7 +228,7 @@ def test_get_download_dir_exception(self, transmission_client): rpc_client.get_session.assert_called_once() @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') - def test_should_get_status(self, transmission_client): + def test_should_get_status_by_hash(self, transmission_client): rpc_client = transmission_client.return_value rpc_client.get_torrent.return_value = namedtuple('Torrent', ('rateDownload', 'totalSize', 'downloadedEver', 'rateUpload') @@ -238,23 +238,23 @@ def test_should_get_status(self, transmission_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - status = plugin.get_download_status('4e2597302ad6b4d7a545c8ec02621ac232316b96') + status = plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') assert status.downloaded_bytes == 6000 assert status.download_speed == 5000 assert status.total_bytes == 10000 assert status.upload_speed == 20 @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') - def test_should_fail_when_no_settings_for_get_torrent(self, transmission_client): + def test_should_fail_when_no_settings_for_get_status_by_hash(self, transmission_client): rpc_client = transmission_client.return_value rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError plugin = TransmissionClientPlugin() - assert plugin.get_download_status('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False + assert plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False rpc_client.get_torrent.assert_not_called() @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') - def test_should_fail_when_get_torrent_fails(self, transmission_client): + def test_should_fail_get_status_by_hash_when_get_torrent_fails(self, transmission_client): rpc_client = transmission_client.return_value rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError @@ -262,6 +262,49 @@ def test_should_fail_when_get_torrent_fails(self, transmission_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_status('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False + assert plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False rpc_client.get_torrent.assert_called_once() + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_get_status(self, transmission_client): + rpc_client = transmission_client.return_value + torrent_id = '4e2597302ad6b4d7a545c8ec02621ac232316b96' + rpc_client.get_torrents.return_value = [namedtuple('Torrent', + ('hashString', 'rateDownload', 'totalSize', 'downloadedEver', + 'rateUpload') + )(torrent_id, 5000, 10000, 6000, 20)] + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + statuses = plugin.get_download_status() + assert torrent_id in statuses + status = statuses[torrent_id] + assert status.downloaded_bytes == 6000 + assert status.download_speed == 5000 + assert status.total_bytes == 10000 + assert status.upload_speed == 20 + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_when_no_settings_for_get_status(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrents.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + assert plugin.get_download_status() is False + rpc_client.get_torrent.assert_not_called() + + @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') + def test_should_fail_get_status_when_get_torrent_fails(self, transmission_client): + rpc_client = transmission_client.return_value + rpc_client.get_torrents.side_effect = transmissionrpc.TransmissionError + + plugin = TransmissionClientPlugin() + settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} + plugin.set_settings(settings) + + assert plugin.get_download_status() is False + + rpc_client.get_torrents.assert_called_once() diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py index bc94ca33..c713cf4b 100644 --- a/tests/plugins/clients/test_utorrent_plugin.py +++ b/tests/plugins/clients/test_utorrent_plugin.py @@ -163,32 +163,61 @@ def test_remove_torrent_success(self, get_mock): self.assertTrue(plugin.remove_torrent(torrent)) @use_vcr - def test_get_download_status_success(self): + def test_get_download_status_by_hash_success(self): plugin = UTorrentClientPlugin() settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, 'password': self.real_password} plugin.set_settings(settings) - torrent = "44E416FCD3DBF967E292B1C1965306EA86FDB74D" - result = plugin.get_download_status(torrent) + torrent = "3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE" + result = plugin.get_download_status_by_hash(torrent) - assert result.upload_speed == 11 - assert result.download_speed == 10 - assert result.downloaded_bytes == 3821779548 - assert result.total_bytes == 3821779548 + assert result.upload_speed == 6277 + assert result.download_speed == 3106179 + assert result.downloaded_bytes == 1251196928 + assert result.total_bytes == 5619792444 - @patch('requests.Session.get') - def test_get_download_status_bad_settings(self, get_mock): + def test_get_download_status_by_hash_bad_settings(self): plugin = UTorrentClientPlugin() torrent = 'torrent' - self.assertFalse(plugin.get_download_status(torrent)) + self.assertFalse(plugin.get_download_status_by_hash(torrent)) @use_vcr - def test_get_download_status_not_found(self): + def test_get_download_status_by_hash_not_found(self): plugin = UTorrentClientPlugin() settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, 'password': self.real_password} plugin.set_settings(settings) torrent = "torrent" - assert plugin.get_download_status(torrent) is False + assert plugin.get_download_status_by_hash(torrent) is False + + @use_vcr + def test_get_download_status_success(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + torrent_hash = "3F6D30E6E4C65684C4ADF8BE45F1F8D71CC4E2AE" + result = plugin.get_download_status() + + assert torrent_hash in result + torrent = result[torrent_hash] + assert torrent.upload_speed == 6277 + assert torrent.download_speed == 3106179 + assert torrent.downloaded_bytes == 1251196928 + assert torrent.total_bytes == 5619792444 + + def test_get_download_status_bad_settings(self): + plugin = UTorrentClientPlugin() + self.assertFalse(plugin.get_download_status()) + + @use_vcr + def test_get_download_status_not_found(self): + plugin = UTorrentClientPlugin() + settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, + 'password': self.real_password} + plugin.set_settings(settings) + + assert plugin.get_download_status() is False diff --git a/tests/rest/test_api_clients.py b/tests/rest/test_api_clients.py index c1f78d61..4fe9bfd6 100644 --- a/tests/rest/test_api_clients.py +++ b/tests/rest/test_api_clients.py @@ -6,7 +6,8 @@ from monitorrent.plugins.clients import DownloadStatus from tests import RestTestBase -from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus +from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus, \ + ClientStatus from monitorrent.plugin_managers import ClientsManager @@ -111,9 +112,9 @@ def test_not_found_update_settings(self): @ddt class TorrentStatusTest(RestTestBase): - def test_get_download_status(self): + def test_get_download_status_by_hash(self): clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) - clients_manager.get_download_status = MagicMock(return_value=DownloadStatus(1, 2, 3, 4)) + clients_manager.get_download_status_by_id = MagicMock(return_value=DownloadStatus(1, 2, 3, 4)) client = TorrentStatus(clients_manager) client.__no_auth__ = True @@ -129,6 +130,38 @@ def test_get_download_status(self): assert result['download_speed'] == 3 assert result['upload_speed'] == 4 +@ddt +class ClientStatusTest(RestTestBase): + def test_get_download_status(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status = MagicMock(return_value={"torrent": DownloadStatus(1, 2, 3, 4)}) + + client = ClientStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/status', client) + + body = self.simulate_request('/api/clients/{0}/status'.format("client"), decode="utf-8") + self.assertEqual(self.srmock.status, falcon.HTTP_OK) + self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type']) + + result = json.loads(body) + torrent = result["torrent"] + assert torrent['downloaded_bytes'] == 1 + assert torrent['total_bytes'] == 2 + assert torrent['download_speed'] == 3 + assert torrent['upload_speed'] == 4 + + def test_check_client_not_found(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status = MagicMock(side_effect=KeyError) + + client = ClientStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/status', client) + + self.simulate_request('/api/clients/{0}/status'.format("client")) + self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + @ddt class CheckClientTest(RestTestBase): diff --git a/tests/test_plugin_managers_clients_manager.py b/tests/test_plugin_managers_clients_manager.py index 8ec28873..9f5ff646 100644 --- a/tests/test_plugin_managers_clients_manager.py +++ b/tests/test_plugin_managers_clients_manager.py @@ -175,17 +175,26 @@ def test_remove_torrent_false(self): remove_torrent_mock1.assert_called_once_with(torrent_hash) remove_torrent_mock2.assert_not_called() - def test_get_download_status_success(self): + def test_get_download_status_by_hash_success(self): result = DownloadStatus(1, 2, 3, 4) - get_download_status1 = MagicMock(return_value=result) - get_download_status2 = MagicMock(return_value=False) + get_download_status_by_hash_1 = MagicMock(return_value=result) + get_download_status_by_hash_2 = MagicMock(return_value=False) torrent_hash = 'hash' - self.client1.get_download_status = get_download_status1 - self.client2.get_download_status = get_download_status2 - actual_result = self.clients_manager.get_download_status(torrent_hash) + self.client1.get_download_status_by_hash = get_download_status_by_hash_1 + self.client2.get_download_status_by_hash = get_download_status_by_hash_2 + actual_result = self.clients_manager.get_download_status_by_id(torrent_hash) + assert actual_result == result + + def test_get_download_status_success(self): + result = {"hash": DownloadStatus(1, 2, 3, 4)} + get_download_status = MagicMock(return_value=result) + + self.client1.get_download_status = get_download_status + actual_result = self.clients_manager.get_download_status(self.CLIENT1_NAME) assert actual_result == result + def test_get_plugins(self): clients = self.clients_manager.clients get_plugins_mock = Mock(return_value=clients) @@ -209,7 +218,7 @@ def test_empty_clients(self): self.assertFalse(clients_amanger.add_torrent('!torrent', None)) self.assertFalse(clients_amanger.find_torrent('hash')) self.assertFalse(clients_amanger.remove_torrent('hash')) - self.assertFalse(clients_amanger.get_download_status('hash')) + self.assertFalse(clients_amanger.get_download_status_by_id('hash')) def test_unknow_client(self): with self.assertRaises(KeyError): From 3d3d95c05a1bbcc37b6346d0cb6898bbcdeca1cb Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 19 Mar 2017 13:26:47 +0300 Subject: [PATCH 04/15] chore(logs): implemented some initial logging --- monitorrent/plugin_managers.py | 9 ++ monitorrent/plugins/clients/deluge.py | 89 ++++++------- monitorrent/plugins/clients/qbittorrent.py | 117 ++++++++---------- monitorrent/plugins/clients/transmission.py | 80 +++++------- monitorrent/plugins/clients/utorrent.py | 75 +++++------ monitorrent/rest/clients.py | 40 +++++- monitorrent/rest/execute.py | 72 ++++++----- monitorrent/rest/new_version.py | 11 +- monitorrent/rest/notifiers.py | 29 ++++- requirements.txt | 1 + server.py | 44 +++++-- tests/plugins/clients/test_deluge_plugin.py | 25 ++-- tests/plugins/clients/test_qbittorrent.py | 20 +-- .../clients/test_transmission_plugin.py | 30 +++-- tests/plugins/clients/test_utorrent_plugin.py | 25 ++-- 15 files changed, 375 insertions(+), 292 deletions(-) diff --git a/monitorrent/plugin_managers.py b/monitorrent/plugin_managers.py index 38ae17c6..2db4cd8a 100644 --- a/monitorrent/plugin_managers.py +++ b/monitorrent/plugin_managers.py @@ -1,4 +1,7 @@ import os + +import structlog + from monitorrent.db import DBSession, row2dict from monitorrent.plugins import Topic from monitorrent.plugins.status import Status @@ -6,11 +9,14 @@ from monitorrent.plugins.trackers import TrackerPluginBase, WithCredentialsMixin from monitorrent.upgrade_manager import add_upgrade + +log = structlog.get_logger() plugins = dict() def load_plugins(plugins_dir="plugins"): file_dir = os.path.dirname(os.path.realpath(__file__)) + module_names = [] for d, dirnames, files in os.walk(os.path.join(file_dir, plugins_dir)): d = d[len(file_dir) + 1:] for f in files: @@ -18,6 +24,8 @@ def load_plugins(plugins_dir="plugins"): continue module_name = os.path.join("monitorrent", d, f[:-3]).replace(os.path.sep, '.') __import__(module_name) + module_names.append(module_name) + log.info("Plugins loaded successfully", modules=module_names) def register_plugin(type, name, instance, upgrade=None): @@ -175,6 +183,7 @@ def __init__(self, clients=None, default_client_name=None): list(self.clients.values())[0] if len(self.clients) > 0 else None) def set_default(self, name): + default_client = self.__get_default_client(name) if default_client is None: raise KeyError() diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index 6a32f28d..6822c492 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -1,5 +1,7 @@ import six import base64 + +import structlog from deluge_client import DelugeRPCClient import pytz from sqlalchemy import Column, Integer, String @@ -9,6 +11,8 @@ from monitorrent.plugins.clients import DownloadStatus +log = structlog.get_logger() + class DelugeCredentials(Base): __tablename__ = "deluge_credentials" @@ -85,30 +89,21 @@ def check_connection(self): client = self._get_client() if not client: return False - try: - client.connect() - return client.connected - except: - return False + client.connect() + return client.connected def get_download_dir(self): client = self._get_client() if not client: return None - try: - client.connect() - return client.call('core.get_config_value', 'move_completed_path').decode('utf-8') - except: - return None + client.connect() + return client.call('core.get_config_value', 'move_completed_path').decode('utf-8') def find_torrent(self, torrent_hash): client = self._get_client() if not client: return False - try: - client.connect() - except: - return False + client.connect() torrent = client.call("core.get_torrent_status", torrent_hash.lower(), ['time_added', 'name']) if len(torrent) == 0: @@ -129,61 +124,49 @@ def add_torrent(self, torrent, torrent_settings): client = self._get_client() if not client: return False - try: - client.connect() - options = None - if torrent_settings is not None: - options = {} - if torrent_settings.download_dir is not None: - options['download_location'] = torrent_settings.download_dir - return client.call("core.add_torrent_file", - None, base64.b64encode(torrent), options) - except: - return False + client.connect() + options = None + if torrent_settings is not None: + options = {} + if torrent_settings.download_dir is not None: + options['download_location'] = torrent_settings.download_dir + return client.call("core.add_torrent_file", + None, base64.b64encode(torrent), options) def remove_torrent(self, torrent_hash): client = self._get_client() if not client: return False - try: - client.connect() - return client.call("core.remove_torrent", - torrent_hash.lower(), False) - except: - return False + client.connect() + return client.call("core.remove_torrent", + torrent_hash.lower(), False) def get_download_status(self): client = self._get_client() if not client: return False - try: - client.connect() - result = client.call("core.get_torrents_status", - {}, ['total_done', 'total_size', 'download_payload_rate', - 'upload_payload_rate', 'state', 'progress']) - statuses = {} - for key, value in result.items(): - statuses[key] = DownloadStatus(value[b'total_done'], value[b'total_size'], - value[b'download_payload_rate'], value[b'upload_payload_rate']) - return statuses - except: - return False + client.connect() + result = client.call("core.get_torrents_status", + {}, ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) + statuses = {} + for key, value in result.items(): + statuses[key] = DownloadStatus(value[b'total_done'], value[b'total_size'], + value[b'download_payload_rate'], value[b'upload_payload_rate']) + return statuses def get_download_status_by_hash(self, torrent_hash): client = self._get_client() lower_hash = torrent_hash.lower() if not client: return False - try: - client.connect() - result = client.call("core.get_torrents_status", - {'hash': lower_hash}, ['total_done', 'total_size', 'download_payload_rate', - 'upload_payload_rate', 'state', 'progress']) - key, value = result.popitem() - return DownloadStatus(value[b'total_done'], value[b'total_size'], - value[b'download_payload_rate'], value[b'upload_payload_rate']) - except: - return False + client.connect() + result = client.call("core.get_torrents_status", + {'hash': lower_hash}, ['total_done', 'total_size', 'download_payload_rate', + 'upload_payload_rate', 'state', 'progress']) + key, value = result.popitem() + return DownloadStatus(value[b'total_done'], value[b'total_size'], + value[b'download_payload_rate'], value[b'upload_payload_rate']) register_plugin('client', 'deluge', DelugeClientPlugin()) diff --git a/monitorrent/plugins/clients/qbittorrent.py b/monitorrent/plugins/clients/qbittorrent.py index 46285326..29f0ac5a 100644 --- a/monitorrent/plugins/clients/qbittorrent.py +++ b/monitorrent/plugins/clients/qbittorrent.py @@ -111,40 +111,34 @@ def find_torrent(self, torrent_hash): if not parameters: return False - try: - # qbittorrent uses case sensitive lower case hash - torrent_hash = torrent_hash.lower() - torrents = parameters['session'].get(parameters['target'] + "query/torrents") - array = json.loads(torrents.text) - torrent = next(torrent for torrent in array if torrent['hash'] == torrent_hash) - if torrent: - time = torrent.get('added_on', None) - result_date = None - if time is not None: - if isinstance(time, six.string_types): - result_date = dateutil.parser.parse(time).replace(tzinfo=reference.LocalTimezone())\ - .astimezone(utc) - else: - result_date = datetime.fromtimestamp(time, utc) - return { - "name": torrent['name'], - "date_added": result_date - } - except Exception as e: - return False + # qbittorrent uses case sensitive lower case hash + torrent_hash = torrent_hash.lower() + torrents = parameters['session'].get(parameters['target'] + "query/torrents") + array = json.loads(torrents.text) + torrent = next(torrent for torrent in array if torrent['hash'] == torrent_hash) + if torrent: + time = torrent.get('added_on', None) + result_date = None + if time is not None: + if isinstance(time, six.string_types): + result_date = dateutil.parser.parse(time).replace(tzinfo=reference.LocalTimezone()) \ + .astimezone(utc) + else: + result_date = datetime.fromtimestamp(time, utc) + return { + "name": torrent['name'], + "date_added": result_date + } def get_download_dir(self): parameters = self._get_params() if not parameters: return None - try: - response = parameters['session'].get(parameters['target'] + 'query/preferences') - response.raise_for_status() - result = response.json() - return six.text_type(result['save_path']) - except: - return None + response = parameters['session'].get(parameters['target'] + 'query/preferences') + response.raise_for_status() + result = response.json() + return six.text_type(result['save_path']) def add_torrent(self, torrent, torrent_settings): """ @@ -154,17 +148,14 @@ def add_torrent(self, torrent, torrent_settings): if not parameters: return False - try: - files = {"torrents": BytesIO(torrent)} - data = None - if torrent_settings is not None: - data = {} - if torrent_settings.download_dir is not None: - data['savepath'] = torrent_settings.download_dir - r = parameters['session'].post(parameters['target'] + "command/upload", data=data, files=files) - return r.status_code == 200 - except: - return False + files = {"torrents": BytesIO(torrent)} + data = None + if torrent_settings is not None: + data = {} + if torrent_settings.download_dir is not None: + data['savepath'] = torrent_settings.download_dir + r = parameters['session'].post(parameters['target'] + "command/upload", data=data, files=files) + return r.status_code == 200 # TODO switch to remove torrent with data def remove_torrent(self, torrent_hash): @@ -172,42 +163,36 @@ def remove_torrent(self, torrent_hash): if not parameters: return False - try: - #qbittorrent uses case sensitive lower case hash - torrent_hash = torrent_hash.lower() - payload = {"hashes": torrent_hash} - r = parameters['session'].post(parameters['target'] + "command/delete", data=payload) - return r.status_code == 200 - except: - return False + # qbittorrent uses case sensitive lower case hash + torrent_hash = torrent_hash.lower() + payload = {"hashes": torrent_hash} + r = parameters['session'].post(parameters['target'] + "command/delete", data=payload) + return r.status_code == 200 def get_download_status(self): parameters = self._get_params() if not parameters: return False - try: - response = parameters['session'].get(parameters['target'] + "query/torrents/") - response.raise_for_status() - result = response.json() - torrents = {} - for torrent in result: - torrents[torrent['hash']] = DownloadStatus(torrent['progress'] * torrent['size'], torrent['size'], torrent['dlspeed'], - torrent['upspeed']) - return torrents - except: - return False + response = parameters['session'].get(parameters['target'] + "query/torrents/") + response.raise_for_status() + result = response.json() + torrents = {} + for torrent in result: + torrents[torrent['hash']] = DownloadStatus(torrent['progress'] * torrent['size'], torrent['size'], + torrent['dlspeed'], + torrent['upspeed']) + return torrents def get_download_status_by_hash(self, torrent_hash): parameters = self._get_params() if not parameters: return False - try: - torrent_hash = torrent_hash.lower() - response = parameters['session'].get(parameters['target'] + "query/propertiesGeneral/" + torrent_hash) - response.raise_for_status() - result = response.json() - return DownloadStatus(result['total_downloaded'], result['total_size'], result['dl_speed'], result['up_speed']) - except: - return False + torrent_hash = torrent_hash.lower() + response = parameters['session'].get(parameters['target'] + "query/propertiesGeneral/" + torrent_hash) + response.raise_for_status() + result = response.json() + return DownloadStatus(result['total_downloaded'], result['total_size'], result['dl_speed'], + result['up_speed']) + register_plugin('client', 'qbittorrent', QBittorrentClientPlugin()) diff --git a/monitorrent/plugins/clients/transmission.py b/monitorrent/plugins/clients/transmission.py index d64b5acb..770cb884 100644 --- a/monitorrent/plugins/clients/transmission.py +++ b/monitorrent/plugins/clients/transmission.py @@ -74,35 +74,26 @@ def check_connection(self): cred = db.query(TransmissionCredentials).first() if not cred: return False - try: - client = transmissionrpc.Client(address=cred.host, port=cred.port, - user=cred.username, password=cred.password) - return client - except transmissionrpc.TransmissionError: - return False + client = transmissionrpc.Client(address=cred.host, port=cred.port, + user=cred.username, password=cred.password) + return client def find_torrent(self, torrent_hash): client = self.check_connection() if not client: return False - try: - torrent = client.get_torrent(torrent_hash.lower(), ['id', 'hashString', 'addedDate', 'name']) - return { - "name": torrent.name, - "date_added": torrent.date_added.replace(tzinfo=reference.LocalTimezone()).astimezone(utc) - } - except KeyError: - return False + torrent = client.get_torrent(torrent_hash.lower(), ['id', 'hashString', 'addedDate', 'name']) + return { + "name": torrent.name, + "date_added": torrent.date_added.replace(tzinfo=reference.LocalTimezone()).astimezone(utc) + } def get_download_dir(self): client = self.check_connection() if not client: return None - try: - session = client.get_session() - return six.text_type(session.download_dir) - except: - return None + session = client.get_session() + return six.text_type(session.download_dir) def add_torrent(self, torrent, torrent_settings): """ @@ -112,53 +103,40 @@ def add_torrent(self, torrent, torrent_settings): client = self.check_connection() if not client: return False - try: - torrent_settings_dict = {} - if torrent_settings is not None: - if torrent_settings.download_dir is not None: - torrent_settings_dict['download_dir'] = torrent_settings.download_dir - - client.add_torrent(base64.b64encode(torrent).decode('utf-8'), **torrent_settings_dict) - return True - except transmissionrpc.TransmissionError: - return False + torrent_settings_dict = {} + if torrent_settings is not None: + if torrent_settings.download_dir is not None: + torrent_settings_dict['download_dir'] = torrent_settings.download_dir + client.add_torrent(base64.b64encode(torrent).decode('utf-8'), **torrent_settings_dict) + return True def remove_torrent(self, torrent_hash): client = self.check_connection() if not client: return False - try: - client.remove_torrent(torrent_hash.lower(), delete_data=False) - return True - except transmissionrpc.TransmissionError: - return False + client.remove_torrent(torrent_hash.lower(), delete_data=False) + return True def get_download_status(self): client = self.check_connection() if not client: return False - try: - torrents = client.get_torrents(None, ['id', 'hashString', 'totalSize', 'downloadedEver', - 'rateDownload', 'rateUpload']) - result = {} - for torrent in torrents: - result[torrent.hashString] = DownloadStatus(torrent.downloadedEver, torrent.totalSize, - torrent.rateDownload, - torrent.rateUpload) - return result - except transmissionrpc.TransmissionError: - return False + torrents = client.get_torrents(None, ['id', 'hashString', 'totalSize', 'downloadedEver', + 'rateDownload', 'rateUpload']) + result = {} + for torrent in torrents: + result[torrent.hashString] = DownloadStatus(torrent.downloadedEver, torrent.totalSize, + torrent.rateDownload, + torrent.rateUpload) + return result def get_download_status_by_hash(self, torrent_hash): client = self.check_connection() if not client: return False - try: - torrent = client.get_torrent(torrent_hash.lower(), ['id', 'hashString', 'totalSize', 'downloadedEver', - 'rateDownload', 'rateUpload']) - return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload) - except transmissionrpc.TransmissionError: - return False + torrent = client.get_torrent(torrent_hash.lower(), ['id', 'hashString', 'totalSize', 'downloadedEver', + 'rateDownload', 'rateUpload']) + return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload) register_plugin('client', 'transmission', TransmissionClientPlugin()) diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index d76b7633..721e839f 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -107,32 +107,26 @@ def find_torrent(self, torrent_hash): return False payload = {"list": '1', "token": parameters["token"]} - try: - torrents = parameters['session'].get(parameters['target'], - params=payload) - array = json.loads(torrents.text)['torrents'] - torrent = next(torrent for torrent in array if torrent[0] == torrent_hash) - if torrent: - return { - "name": torrent[2], - # date added not supported by web api - "date_added": None - } - except: - return False + torrents = parameters['session'].get(parameters['target'], + params=payload) + array = json.loads(torrents.text)['torrents'] + torrent = next(torrent for torrent in array if torrent[0] == torrent_hash) + if torrent: + return { + "name": torrent[2], + # date added not supported by web api + "date_added": None + } def add_torrent(self, torrent, torrent_settings): parameters = self._get_params() if not parameters: return False - try: - payload = {"action": "add-file", "token": parameters["token"]} - files = {"torrent_file": BytesIO(torrent)} - r = parameters['session'].post(parameters['target'], params=payload, files=files) - return r.status_code == 200 - except: - return False + payload = {"action": "add-file", "token": parameters["token"]} + files = {"torrent_file": BytesIO(torrent)} + r = parameters['session'].post(parameters['target'], params=payload, files=files) + return r.status_code == 200 # TODO switch to remove torrent with data def remove_torrent(self, torrent_hash): @@ -140,12 +134,9 @@ def remove_torrent(self, torrent_hash): if not parameters: return False - try: - payload = {"action": "remove", "hash": torrent_hash, "token": parameters["token"]} - parameters['session'].get(parameters['target'], params=payload) - return True - except: - return False + payload = {"action": "remove", "hash": torrent_hash, "token": parameters["token"]} + parameters['session'].get(parameters['target'], params=payload) + return True def get_download_status(self): parameters = self._get_params() @@ -153,16 +144,13 @@ def get_download_status(self): return False payload = {"list": '1', "token": parameters["token"]} - try: - torrents = parameters['session'].get(parameters['target'], - params=payload) - array = json.loads(torrents.text)['torrents'] - result = {} - for torrent in array: - result[torrent[0]] = DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) - return result - except: - return False + torrents = parameters['session'].get(parameters['target'], + params=payload) + array = json.loads(torrents.text)['torrents'] + result = {} + for torrent in array: + result[torrent[0]] = DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) + return result def get_download_status_by_hash(self, torrent_hash): parameters = self._get_params() @@ -170,15 +158,12 @@ def get_download_status_by_hash(self, torrent_hash): return False payload = {"list": '1', "token": parameters["token"]} - try: - torrents = parameters['session'].get(parameters['target'], - params=payload) - array = json.loads(torrents.text)['torrents'] - torrent = next(torrent for torrent in array if torrent[0] == torrent_hash) - if torrent: - return DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) - except: - return False + torrents = parameters['session'].get(parameters['target'], + params=payload) + array = json.loads(torrents.text)['torrents'] + torrent = next(torrent for torrent in array if torrent[0] == torrent_hash) + if torrent: + return DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) register_plugin('client', 'utorrent', UTorrentClientPlugin()) diff --git a/monitorrent/rest/clients.py b/monitorrent/rest/clients.py index e23a0ad4..5109aeb9 100644 --- a/monitorrent/rest/clients.py +++ b/monitorrent/rest/clients.py @@ -2,9 +2,13 @@ from builtins import str from builtins import object import falcon +import structlog + from monitorrent.plugin_managers import ClientsManager from monitorrent.settings_manager import SettingsManager +log = structlog.get_logger() + # noinspection PyUnusedLocal class ClientCollection(object): @@ -15,8 +19,12 @@ def __init__(self, clients_manager): self.clients_manager = clients_manager def on_get(self, req, resp): - resp.json = [{'name': name, 'form': client.form, 'is_default': self.clients_manager.get_default() == client} - for name, client in list(self.clients_manager.clients.items())] + try: + resp.json = [{'name': name, 'form': client.form, 'is_default': self.clients_manager.get_default() == client} + for name, client in list(self.clients_manager.clients.items())] + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) # noinspection PyUnusedLocal @@ -33,7 +41,11 @@ def on_get(self, req, resp, client): if not result: result = {} except KeyError as e: + log.error("Client could not be found", client=client, exception=str(e)) raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.json = result def on_put(self, req, resp, client): @@ -41,7 +53,11 @@ def on_put(self, req, resp, client): try: self.clients_manager.set_settings(client, settings) except KeyError as e: + log.error("Client could not be found", client=client, exception=str(e)) raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_NO_CONTENT @@ -57,7 +73,11 @@ def on_get(self, req, resp, torrent_hash): :type torrent_hash: str """ - resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ + try: + resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) # noinspection PyUnusedLocal @@ -72,7 +92,11 @@ def on_get(self, req, resp, client): try: resp.json = {'status': True if self.clients_manager.check_connection(client) else False} except KeyError as e: + log.error("Client could not be found", client=client, exception=str(e)) raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_OK @@ -90,6 +114,7 @@ def __init__(self, clients_manager): def on_get(self, req, resp): default_client = self.clients_manager.get_default() if default_client is None: + log.error("Default client not set") raise falcon.HTTPNotFound(title='Default plugin not set') supported_feilds = default_client.SUPPORTED_FIELDS if hasattr(default_client, 'SUPPORTED_FIELDS') else [] @@ -122,7 +147,11 @@ def on_put(self, req, resp, client): try: self.clients_manager.set_default(client) except KeyError as e: + log.error("Client could not be found", client=client, exception=str(e)) raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_NO_CONTENT @@ -141,6 +170,9 @@ def on_get(self, req, resp, client): result_dict[key] = value.__dict__ resp.json = result_dict except KeyError as e: + log.error("Client could not be found", client=client, exception=str(e)) raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_OK - diff --git a/monitorrent/rest/execute.py b/monitorrent/rest/execute.py index aefef950..0b35f1f9 100644 --- a/monitorrent/rest/execute.py +++ b/monitorrent/rest/execute.py @@ -1,9 +1,13 @@ from builtins import object import time import falcon +import structlog + from monitorrent.plugins.status import Status from monitorrent.engine import EngineRunner, ExecuteLogManager +log = structlog.get_logger() + # noinspection PyUnusedLocal class ExecuteLogCurrent(object): @@ -14,16 +18,20 @@ def __init__(self, log_manager): self.log_manager = log_manager def on_get(self, req, resp): - after = req.get_param_as_int('after', required=False) - - start = time.time() - result = [] - while True: - result = self.log_manager.get_current_execute_log_details(after) or [] - if len(result) == 0 and time.time() - start < 30: - time.sleep(0.1) - else: - break + try: + after = req.get_param_as_int('after', required=False) + + start = time.time() + result = [] + while True: + result = self.log_manager.get_current_execute_log_details(after) or [] + if len(result) == 0 and time.time() - start < 30: + time.sleep(0.1) + else: + break + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.json = {'is_running': self.log_manager.is_running(), 'logs': result} @@ -37,23 +45,27 @@ def __init__(self, engine_runner): self.engine_runner = engine_runner def on_post(self, req, resp): - params = {} - req.get_param_as_list('ids', transform=int, store=params) - req.get_param_as_list('statuses', transform=Status.parse, store=params) - req.get_param('tracker', store=params) - if len(params) > 1: - raise falcon.HTTPBadRequest("wrong params count", - 'Only one of params are supported: ids, statuses or tracker, ' + - 'but {0} was provided'.format(', '.join(params.keys()))) - if 'ids' in params: - ids = params['ids'] - elif 'statuses' in params: - ids = self.engine_runner.trackers_manager.get_status_topics_ids(params['statuses']) - elif 'tracker' in params: - topics = self.engine_runner.trackers_manager.get_tracker_topics(params['tracker']) - ids = [topic.id for topic in topics] - else: - ids = None - if ids is not None and len(ids) == 0: - raise falcon.HTTPConflict("Can't get any ids", "This request doesn't produce any topics for execute") - self.engine_runner.execute(ids) + try: + params = {} + req.get_param_as_list('ids', transform=int, store=params) + req.get_param_as_list('statuses', transform=Status.parse, store=params) + req.get_param('tracker', store=params) + if len(params) > 1: + raise falcon.HTTPBadRequest("wrong params count", + 'Only one of params are supported: ids, statuses or tracker, ' + + 'but {0} was provided'.format(', '.join(params.keys()))) + if 'ids' in params: + ids = params['ids'] + elif 'statuses' in params: + ids = self.engine_runner.trackers_manager.get_status_topics_ids(params['statuses']) + elif 'tracker' in params: + topics = self.engine_runner.trackers_manager.get_tracker_topics(params['tracker']) + ids = [topic.id for topic in topics] + else: + ids = None + if ids is not None and len(ids) == 0: + raise falcon.HTTPConflict("Can't get any ids", "This request doesn't produce any topics for execute") + self.engine_runner.execute(ids) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) diff --git a/monitorrent/rest/new_version.py b/monitorrent/rest/new_version.py index d9caa712..c4b64edd 100644 --- a/monitorrent/rest/new_version.py +++ b/monitorrent/rest/new_version.py @@ -1,5 +1,10 @@ +import falcon +import structlog + from monitorrent.new_version_checker import NewVersionChecker +log = structlog.get_logger() + # noinspection PyUnusedLocal class NewVersion(object): @@ -10,4 +15,8 @@ def __init__(self, new_version_checker): self.new_version_checker = new_version_checker def on_get(self, req, resp): - resp.json = {'url': self.new_version_checker.new_version_url} + try: + resp.json = {'url': self.new_version_checker.new_version_url} + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) diff --git a/monitorrent/rest/notifiers.py b/monitorrent/rest/notifiers.py index 9d01c4a7..4b19e407 100644 --- a/monitorrent/rest/notifiers.py +++ b/monitorrent/rest/notifiers.py @@ -3,8 +3,12 @@ import falcon from builtins import str from builtins import object + +import structlog + from monitorrent.plugin_managers import NotifierManager +log = structlog.get_logger() # noinspection PyUnusedLocal class NotifierCollection(object): @@ -15,11 +19,15 @@ def __init__(self, notifier_manager): self.notifier_manager = notifier_manager def on_get(self, req, resp): - resp.json = [{'name': name, + try: + resp.json = [{'name': name, 'form': notifier.form, 'enabled': self.notifier_manager.get_enabled(name), 'has_settings': self.notifier_manager.get_settings(name) is not None} for name, notifier in list(self.notifier_manager.notifiers.items())] + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) # noinspection PyUnusedLocal @@ -37,7 +45,11 @@ def on_get(self, req, resp, notifier): resp.json = {} return except KeyError as e: + log.error("Notifier could not be found", notifier=notifier, exception=str(e)) raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.json = result.__props__() def on_put(self, req, resp, notifier): @@ -45,10 +57,16 @@ def on_put(self, req, resp, notifier): try: updated = self.notifier_manager.update_settings(notifier, settings) except KeyError as e: + log.error("Notifier could not be found", notifier=notifier, exception=str(e)) raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) if not updated: + log.error("Notifier plugin doesn't support settings", notifier=notifier) raise falcon.HTTPBadRequest('NotSettable', 'Notifier plugin \'{0}\' doesn\'t support settings' .format(notifier)) + resp.status = falcon.HTTP_NO_CONTENT @@ -63,7 +81,11 @@ def on_get(self, req, resp, notifier): try: resp.json = {'status': True if self.notifier_manager.send_test_message(notifier) else False} except KeyError as e: + log.error("Notifier could not be found", notifier=notifier, exception=str(e)) raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_OK @@ -80,8 +102,13 @@ def on_put(self, req, resp, notifier): enabled = params['enabled'] updated = self.notifier_manager.set_enabled(notifier, enabled) except KeyError as e: + log.error("Notifier could not be found", notifier=notifier, exception=str(e)) raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) + except Exception as e: + log.error("An error has occurred", exception=str(e)) + raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) if not updated: + log.error("Notifier plugin doesn't support settings", notifier=notifier) raise falcon.HTTPBadRequest('NotSettable', 'Notifier plugin \'{0}\' doesn\'t support settings' .format(notifier)) resp.status = falcon.HTTP_NO_CONTENT diff --git a/requirements.txt b/requirements.txt index 17af900e..afd7b4e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,7 @@ html5lib==0.9999999 dateutils phpserialize semver +structlog #python 2/3 compatibility and backports six diff --git a/server.py b/server.py index 83849315..f7d5d5b2 100644 --- a/server.py +++ b/server.py @@ -2,11 +2,16 @@ from builtins import range import os import sys + +import logging import six import random import string import argparse import warnings + +import structlog +from structlog.stdlib import LoggerFactory from cheroot import wsgi from monitorrent.engine import DBEngineRunner, DbLoggerWrapper, ExecuteLogManager from monitorrent.db import init_db_engine, create_db @@ -35,8 +40,26 @@ from monitorrent.rest.execute_logs import ExecuteLogs from monitorrent.rest.execute_logs_details import ExecuteLogsDetails - -def add_static_route(api, files_dir): +structlog.configure( + processors=[ + structlog.stdlib.filter_by_level, + structlog.stdlib.add_logger_name, + structlog.stdlib.add_log_level, + structlog.stdlib.PositionalArgumentsFormatter(), + structlog.processors.TimeStamper(fmt="iso"), + structlog.processors.StackInfoRenderer(), + structlog.processors.format_exc_info, + structlog.processors.JSONRenderer() + ], + context_class=dict, + logger_factory=LoggerFactory(), + wrapper_class=structlog.stdlib.BoundLogger, + cache_logger_on_first_use=True +) + + +def add_static_route(api, files_dir, log): + log.debug('Adding static routes', dir=files_dir) file_dir = os.path.dirname(os.path.realpath(__file__)) static_dir = os.path.join(file_dir, files_dir) api.add_route('/', StaticFiles(static_dir, 'index.html')) @@ -50,10 +73,10 @@ def add_static_route(api, files_dir): def create_app(secret_key, token, tracker_manager, clients_manager, notifier_manager, settings_manager, - engine_runner, log_manager, new_version_checker): + engine_runner, log_manager, new_version_checker, log): AuthMiddleware.init(secret_key, token, lambda: settings_manager.get_is_authentication_enabled()) app = create_api() - add_static_route(app, 'webapp') + add_static_route(app, 'webapp', log) app.add_route('/api/login', Login(settings_manager)) app.add_route('/api/logout', Logout()) app.add_route('/api/topics', TopicCollection(tracker_manager)) @@ -114,6 +137,7 @@ def __init__(self, parsed_args): config_path = parsed_args.config or self.config if os.path.isfile(config_path): # noinspection PyBroadException + log.bind(config_is_file=True) try: parsed_config = {} with open(config_path) as config_file: @@ -148,7 +172,13 @@ def __init__(self, parsed_args): parsed_args = parser.parse_args() config = Config(parsed_args) - + if config.debug: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.WARN) + log = structlog.get_logger() + if log.isEnabledFor(logging.INFO): + log.info("Configuration finished", config=config.__dict__) db_connection_string = "sqlite:///" + config.db_path init_db_engine(db_connection_string, False) @@ -186,7 +216,7 @@ def __init__(self, parsed_args): token = ''.join(random.choice(string.ascii_letters) for _ in range(8)) app = create_app(secret_key, token, tracker_manager, clients_manager, notifier_manager, settings_manager, - engine_runner, log_manager, new_version_checker) + engine_runner, log_manager, new_version_checker, log) server_start_params = (config.ip, config.port) server = wsgi.Server(server_start_params, app) print('Server started on {0}:{1}'.format(*server_start_params)) @@ -202,6 +232,6 @@ def __init__(self, parsed_args): print('Server stopped') + if __name__ == '__main__': main() - diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py index 854114a0..21a58ea0 100644 --- a/tests/plugins/clients/test_deluge_plugin.py +++ b/tests/plugins/clients/test_deluge_plugin.py @@ -1,6 +1,8 @@ import base64 import time from datetime import datetime + +import pytest from mock import patch from ddt import ddt, data import pytz @@ -61,7 +63,8 @@ def test_check_connection_connect_exception(self, deluge_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - self.assertFalse(plugin.check_connection()) + with pytest.raises(Exception) as e: + plugin.check_connection() deluge_client.assert_called_with('localhost', DelugeClientPlugin.DEFAULT_PORT, 'monitorrent', 'monitorrent') @@ -96,7 +99,7 @@ def test_find_torrent_without_credentials(self, deluge_client): plugin = DelugeClientPlugin() torrent_hash = 'SomeRandomHashMockString' - self.assertFalse(plugin.find_torrent(torrent_hash)) + assert plugin.find_torrent(torrent_hash) == False rpc_client.call.assert_not_called() @@ -111,7 +114,8 @@ def test_find_torrent_connect_exception(self, deluge_client): plugin.set_settings(settings) torrent_hash = 'SomeRandomHashMockString' - self.assertFalse(plugin.find_torrent(torrent_hash)) + with pytest.raises(Exception) as e: + plugin.find_torrent(torrent_hash) rpc_client.call.assert_not_called() @@ -191,7 +195,8 @@ def test_add_torrent_call_exception(self, deluge_client): plugin.set_settings(settings) torrent = b'!torrent.content' - self.assertFalse(plugin.add_torrent(torrent, None)) + with pytest.raises(Exception) as e: + plugin.add_torrent(torrent, None) rpc_client.call.assert_called_once_with('core.add_torrent_file', None, base64.b64encode(torrent), None) @@ -236,7 +241,8 @@ def test_remove_torrent_call_exception(self, deluge_client): plugin.set_settings(settings) torrent_hash = 'SomeRandomHashMockString' - self.assertFalse(plugin.remove_torrent(torrent_hash)) + with pytest.raises(Exception) as e: + plugin.remove_torrent(torrent_hash) rpc_client.call.assert_called_once_with('core.remove_torrent', torrent_hash.lower(), False) @@ -267,7 +273,8 @@ def test_get_download_dir_exception(self, deluge_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_dir() is None + with pytest.raises(Exception) as e: + plugin.get_download_dir() rpc_client.call.assert_called_once_with('core.get_config_value', 'move_completed_path') @@ -317,7 +324,8 @@ def test_should_fail_get_torrent_status_by_hash_on_error(self, deluge_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_status_by_hash(torrent_hash) is False + with pytest.raises(Exception) as e: + plugin.get_download_status_by_hash(torrent_hash) rpc_client.call.assert_called_once_with("core.get_torrents_status", {'hash': torrent_hash.lower()}, @@ -366,7 +374,8 @@ def test_should_fail_get_torrent_status_on_error(self, deluge_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_status() is False + with pytest.raises(Exception) as e: + plugin.get_download_status() rpc_client.call.assert_called_once_with("core.get_torrents_status", {}, ['total_done', 'total_size', 'download_payload_rate', diff --git a/tests/plugins/clients/test_qbittorrent.py b/tests/plugins/clients/test_qbittorrent.py index edf34770..82066a85 100644 --- a/tests/plugins/clients/test_qbittorrent.py +++ b/tests/plugins/clients/test_qbittorrent.py @@ -1,5 +1,6 @@ from datetime import datetime +import pytest import pytz import json from ddt import ddt @@ -102,8 +103,8 @@ def test_find_torrent_failed(self, post_mock, get_mock): settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, 'password': self.real_password} plugin.set_settings(settings) - torrent = plugin.find_torrent(torrent_hash) - self.assertFalse(torrent) + with pytest.raises(Exception) as e: + torrent = plugin.find_torrent(torrent_hash) def test_find_torrent_no_settings(self): with patch.object(monitorrent.plugins.clients.qbittorrent.requests.Session, 'post', side_effect=Exception): @@ -134,7 +135,8 @@ def test_add_torrent_failed(self, post_mock): plugin.set_settings(settings) torrent = b'torrent' - self.assertFalse(plugin.add_torrent(torrent, None)) + with pytest.raises(Exception) as e: + plugin.add_torrent(torrent, None) @patch('requests.Session.post') def test_add_torrent_success(self, post_mock): @@ -189,7 +191,8 @@ def test_remove_torrent_failed(self, post_mock): plugin.set_settings(settings) torrent = b'torrent' - self.assertFalse(plugin.remove_torrent(torrent)) + with pytest.raises(Exception) as e: + plugin.remove_torrent(torrent) @patch('requests.Session.post') def test_remove_torrent_success(self, post_mock): @@ -242,7 +245,8 @@ def test_get_download_dir_error(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - assert plugin.get_download_dir() is None + with pytest.raises(Exception) as e: + plugin.get_download_dir() @Mocker() def test_should_get_download_status_by_hash_successfully(self, mocker): @@ -318,7 +322,8 @@ def test_should_fail_when_download_status_by_hash_wasnt_retrieved(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - assert plugin.get_download_status_by_hash(hash_string) is False + with pytest.raises(Exception) as e: + plugin.get_download_status_by_hash(hash_string) @Mocker() def test_should_get_download_status_successfully(self, mocker): @@ -386,4 +391,5 @@ def test_should_fail_when_download_status_wasnt_retrieved(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - assert plugin.get_download_status() is False + with pytest.raises(Exception) as e: + plugin.get_download_status() diff --git a/tests/plugins/clients/test_transmission_plugin.py b/tests/plugins/clients/test_transmission_plugin.py index 13b52c05..22ab121d 100644 --- a/tests/plugins/clients/test_transmission_plugin.py +++ b/tests/plugins/clients/test_transmission_plugin.py @@ -1,6 +1,8 @@ import base64 from collections import namedtuple from datetime import datetime + +import pytest from mock import patch, Mock from ddt import ddt import transmissionrpc @@ -53,7 +55,8 @@ def test_check_connection_connect_exception(self, transmission_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - self.assertFalse(plugin.check_connection()) + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.check_connection() transmission_client.assert_called_with(address='localhost', port=TransmissionClientPlugin.DEFAULT_PORT, user='monitorrent', password='monitorrent') @@ -87,7 +90,7 @@ def test_find_torrent_without_credentials(self, transmission_client): plugin = TransmissionClientPlugin() torrent_hash = 'SomeRandomHashMockString' - self.assertFalse(plugin.find_torrent(torrent_hash)) + assert plugin.find_torrent(torrent_hash) is False rpc_client.get_torrent.assert_not_called() @@ -101,7 +104,8 @@ def test_find_torrent_get_torrent_exception(self, transmission_client): torrent_hash = 'SomeRandomHashMockString' rpc_client.get_torrent.side_effect = KeyError - self.assertFalse(plugin.find_torrent(torrent_hash)) + with pytest.raises(KeyError) as e: + plugin.find_torrent(torrent_hash) rpc_client.get_torrent.assert_called_once_with(torrent_hash.lower(), ['id', 'hashString', 'addedDate', 'name']) @@ -156,7 +160,8 @@ def test_add_torrent_add_torrent_exception(self, transmission_client): plugin.set_settings(settings) torrent = b'!torrent.content' - self.assertFalse(plugin.add_torrent(torrent, None)) + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.add_torrent(torrent, None) rpc_client.add_torrent.assert_called_once_with(base64.b64encode(torrent).decode('utf-8')) @@ -180,7 +185,7 @@ def test_remove_torrent_without_credentials(self, transmission_client): plugin = TransmissionClientPlugin() torrent_hash = 'SomeRandomHashMockString' - self.assertFalse(plugin.remove_torrent(torrent_hash)) + assert plugin.remove_torrent(torrent_hash) is False rpc_client.remove_torrent.assert_not_called() @@ -194,7 +199,8 @@ def test_remove_torrent_remove_torrent_exception(self, transmission_client): plugin.set_settings(settings) torrent_hash = 'SomeRandomHashMockString' - self.assertFalse(plugin.remove_torrent(torrent_hash)) + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.remove_torrent(torrent_hash) rpc_client.remove_torrent.assert_called_once_with(torrent_hash.lower(), delete_data=False) @@ -223,7 +229,8 @@ def test_get_download_dir_exception(self, transmission_client): settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_dir() is None + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.get_download_dir() rpc_client.get_session.assert_called_once() @@ -250,7 +257,7 @@ def test_should_fail_when_no_settings_for_get_status_by_hash(self, transmission_ rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError plugin = TransmissionClientPlugin() - assert plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False + assert plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') == False rpc_client.get_torrent.assert_not_called() @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') @@ -262,7 +269,8 @@ def test_should_fail_get_status_by_hash_when_get_torrent_fails(self, transmissio settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') is False + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') rpc_client.get_torrent.assert_called_once() @@ -293,6 +301,7 @@ def test_should_fail_when_no_settings_for_get_status(self, transmission_client): rpc_client.get_torrents.side_effect = transmissionrpc.TransmissionError plugin = TransmissionClientPlugin() + assert plugin.get_download_status() is False rpc_client.get_torrent.assert_not_called() @@ -305,6 +314,7 @@ def test_should_fail_get_status_when_get_torrent_fails(self, transmission_client settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} plugin.set_settings(settings) - assert plugin.get_download_status() is False + with pytest.raises(transmissionrpc.TransmissionError) as e: + plugin.get_download_status() rpc_client.get_torrents.assert_called_once() diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py index c713cf4b..06334179 100644 --- a/tests/plugins/clients/test_utorrent_plugin.py +++ b/tests/plugins/clients/test_utorrent_plugin.py @@ -1,3 +1,6 @@ +from json import JSONDecodeError + +import pytest from ddt import ddt from mock import patch, Mock, MagicMock from requests import Response @@ -77,8 +80,8 @@ def test_find_torrent_failed(self, get_mock): settings = {'host': self.real_host, 'port': self.real_port, 'username': self.real_login, 'password': self.real_password} plugin.set_settings(settings) - torrent = plugin.find_torrent(torrent_hash) - self.assertFalse(torrent) + with pytest.raises(JSONDecodeError) as e: + plugin.find_torrent(torrent_hash) @patch('requests.Session.get') def test_add_torrent_bad_settings(self, get_mock): @@ -102,7 +105,8 @@ def test_add_torrent_failed(self, post_mock, get_mock): plugin.set_settings(settings) torrent = b'torrent' - self.assertFalse(plugin.add_torrent(torrent, None)) + with pytest.raises(Exception) as e: + plugin.add_torrent(torrent, None) @patch('requests.Session.get') @patch('requests.Session.post') @@ -127,7 +131,7 @@ def test_add_torrent_success(self, post_mock, get_mock): def test_remove_torrent_bad_settings(self, get_mock): plugin = UTorrentClientPlugin() torrent = b'torrent' - self.assertFalse(plugin.remove_torrent(torrent)) + assert plugin.remove_torrent(torrent) is False @patch('requests.Session.get') def test_remove_torrent_failed(self, get_mock): @@ -143,7 +147,8 @@ def test_remove_torrent_failed(self, get_mock): plugin.set_settings(settings) torrent = b'torrent' - self.assertFalse(plugin.remove_torrent(torrent)) + with pytest.raises(Exception) as e: + plugin.remove_torrent(torrent) @patch('requests.Session.get') def test_remove_torrent_success(self, get_mock): @@ -180,7 +185,7 @@ def test_get_download_status_by_hash_success(self): def test_get_download_status_by_hash_bad_settings(self): plugin = UTorrentClientPlugin() torrent = 'torrent' - self.assertFalse(plugin.get_download_status_by_hash(torrent)) + assert plugin.get_download_status_by_hash(torrent) is False @use_vcr def test_get_download_status_by_hash_not_found(self): @@ -190,7 +195,8 @@ def test_get_download_status_by_hash_not_found(self): plugin.set_settings(settings) torrent = "torrent" - assert plugin.get_download_status_by_hash(torrent) is False + with pytest.raises(Exception) as e: + plugin.get_download_status_by_hash(torrent) @use_vcr def test_get_download_status_success(self): @@ -211,7 +217,7 @@ def test_get_download_status_success(self): def test_get_download_status_bad_settings(self): plugin = UTorrentClientPlugin() - self.assertFalse(plugin.get_download_status()) + assert plugin.get_download_status() is False @use_vcr def test_get_download_status_not_found(self): @@ -220,4 +226,5 @@ def test_get_download_status_not_found(self): 'password': self.real_password} plugin.set_settings(settings) - assert plugin.get_download_status() is False + with pytest.raises(Exception) as e: + plugin.get_download_status() From ad2c9047bde6dce136253da24dcc107348e532ca Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Wed, 22 Mar 2017 23:24:56 +0300 Subject: [PATCH 05/15] chore(logs): added some execute logs --- monitorrent/engine.py | 12 +++++++++++- monitorrent/rest/clients.py | 14 +++++++------- monitorrent/rest/execute.py | 4 ++-- monitorrent/rest/new_version.py | 2 +- monitorrent/rest/notifiers.py | 10 +++++----- server.py | 3 +-- 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/monitorrent/engine.py b/monitorrent/engine.py index e0b0d0a8..738e2c9a 100644 --- a/monitorrent/engine.py +++ b/monitorrent/engine.py @@ -1,4 +1,6 @@ import sys + +import logging import six import threading import traceback @@ -10,11 +12,14 @@ import pytz import html +import structlog from sqlalchemy import Column, Integer, ForeignKey, Unicode, Enum, func from monitorrent.db import Base, DBSession, row2dict, UTCDateTime from monitorrent.utils.timers import timer from monitorrent.plugins.status import Status +log = structlog.get_logger() + class Logger(object): def started(self, start_time): @@ -116,11 +121,13 @@ def execute(self, ids): if len(tracker_topics) == 0: return + log.info("Tracker topics mapping constructed", mapping=tracker_topics) with self.notifier_manager.execute() as notifier_manager_execute: with self.start(execute_trackers, notifier_manager_execute) as engine_trackers: for name, tracker, topics in tracker_topics: tracker.init(tracker_settings) with engine_trackers.start(name) as engine_tracker: + log.info("Executing tracker", name=name, topics=topics) tracker.execute(topics, engine_tracker) @@ -374,7 +381,7 @@ def info(self, message): def failed(self, message, exc_type=None, exc_value=None, exc_tb=None): if exc_value is not None: formatted_exception = u''.join(traceback.format_exception(exc_type, exc_value, exc_tb)) - failed_message = u'{0}
{1}
'\ + failed_message = u'{0}
{1}
' \ .format(message, html.escape(formatted_exception).replace(u'\n', u'
')) else: failed_message = message @@ -588,16 +595,19 @@ def _execute(self, ids=None): caught_exception = None self.is_executing = True try: + log.info("Starting execute", time=str(datetime.now())) self.logger.started(datetime.now(pytz.utc)) engine = Engine(self.logger, self.settings_manager, self.trackers_manager, self.clients_manager, self.notifier_manager) engine.execute(ids) except: caught_exception = sys.exc_info()[0] + log.error("An error has occurred during execute", exception=str(caught_exception)) finally: self.is_executing = False self.last_execute = datetime.now(pytz.utc) self.logger.finished(self.last_execute, caught_exception) + log.info("Ending execute", time=str(datetime.now())) return True @staticmethod diff --git a/monitorrent/rest/clients.py b/monitorrent/rest/clients.py index 5109aeb9..ef18c884 100644 --- a/monitorrent/rest/clients.py +++ b/monitorrent/rest/clients.py @@ -24,7 +24,7 @@ def on_get(self, req, resp): for name, client in list(self.clients_manager.clients.items())] except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) # noinspection PyUnusedLocal @@ -45,7 +45,7 @@ def on_get(self, req, resp, client): raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.json = result def on_put(self, req, resp, client): @@ -57,7 +57,7 @@ def on_put(self, req, resp, client): raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_NO_CONTENT @@ -77,7 +77,7 @@ def on_get(self, req, resp, torrent_hash): resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) # noinspection PyUnusedLocal @@ -96,7 +96,7 @@ def on_get(self, req, resp, client): raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_OK @@ -151,7 +151,7 @@ def on_put(self, req, resp, client): raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_NO_CONTENT @@ -174,5 +174,5 @@ def on_get(self, req, resp, client): raise falcon.HTTPNotFound(title='Client plugin \'{0}\' not found'.format(client), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_OK diff --git a/monitorrent/rest/execute.py b/monitorrent/rest/execute.py index 0b35f1f9..87e48e08 100644 --- a/monitorrent/rest/execute.py +++ b/monitorrent/rest/execute.py @@ -31,7 +31,7 @@ def on_get(self, req, resp): break except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.json = {'is_running': self.log_manager.is_running(), 'logs': result} @@ -68,4 +68,4 @@ def on_post(self, req, resp): self.engine_runner.execute(ids) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise diff --git a/monitorrent/rest/new_version.py b/monitorrent/rest/new_version.py index c4b64edd..d9d10cec 100644 --- a/monitorrent/rest/new_version.py +++ b/monitorrent/rest/new_version.py @@ -19,4 +19,4 @@ def on_get(self, req, resp): resp.json = {'url': self.new_version_checker.new_version_url} except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) diff --git a/monitorrent/rest/notifiers.py b/monitorrent/rest/notifiers.py index 4b19e407..db746eb7 100644 --- a/monitorrent/rest/notifiers.py +++ b/monitorrent/rest/notifiers.py @@ -27,7 +27,7 @@ def on_get(self, req, resp): for name, notifier in list(self.notifier_manager.notifiers.items())] except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) # noinspection PyUnusedLocal @@ -49,7 +49,7 @@ def on_get(self, req, resp, notifier): raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.json = result.__props__() def on_put(self, req, resp, notifier): @@ -61,7 +61,7 @@ def on_put(self, req, resp, notifier): raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) if not updated: log.error("Notifier plugin doesn't support settings", notifier=notifier) raise falcon.HTTPBadRequest('NotSettable', 'Notifier plugin \'{0}\' doesn\'t support settings' @@ -85,7 +85,7 @@ def on_get(self, req, resp, notifier): raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.status = falcon.HTTP_OK @@ -106,7 +106,7 @@ def on_put(self, req, resp, notifier): raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) except Exception as e: log.error("An error has occurred", exception=str(e)) - raise falcon.HTTP_INTERNAL_SERVER_ERROR(title='A server has encountered an error', description=str(e)) + raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) if not updated: log.error("Notifier plugin doesn't support settings", notifier=notifier) raise falcon.HTTPBadRequest('NotSettable', 'Notifier plugin \'{0}\' doesn\'t support settings' diff --git a/server.py b/server.py index f7d5d5b2..07b9ddf2 100644 --- a/server.py +++ b/server.py @@ -177,8 +177,7 @@ def __init__(self, parsed_args): else: logging.basicConfig(level=logging.WARN) log = structlog.get_logger() - if log.isEnabledFor(logging.INFO): - log.info("Configuration finished", config=config.__dict__) + log.info("Configuration finished", config=config.__dict__) db_connection_string = "sqlite:///" + config.db_path init_db_engine(db_connection_string, False) From 50a8c78c4c4b4a0ad4621669e48b07b664b60050 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 2 Apr 2017 20:45:13 +0300 Subject: [PATCH 06/15] feat(plugins/clients): added more info retrieved from clients. --- monitorrent/plugins/clients/__init__.py | 31 +++++++++++++++- monitorrent/plugins/clients/deluge.py | 36 +++++++++++++++--- monitorrent/plugins/clients/downloader.py | 6 +-- monitorrent/plugins/clients/qbittorrent.py | 27 ++++++++++++-- monitorrent/plugins/clients/transmission.py | 26 +++++++++---- monitorrent/plugins/clients/utorrent.py | 37 +++++++++++++++++-- monitorrent/rest/notifiers.py | 9 +++-- tests/plugins/clients/test_deluge_plugin.py | 16 ++++++-- .../clients/test_transmission_plugin.py | 9 +++-- tests/rest/test_api_clients.py | 10 +++-- tests/test_plugin_managers_clients_manager.py | 6 +-- 11 files changed, 172 insertions(+), 41 deletions(-) diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py index 3441d9ce..62d4f939 100644 --- a/monitorrent/plugins/clients/__init__.py +++ b/monitorrent/plugins/clients/__init__.py @@ -1,12 +1,41 @@ +from enum import Enum + from monitorrent.plugins.trackers import Topic +class TorrentDownloadStatus(Enum): + Queued = 1, + Preparing = 2, + Checking = 3, + Downloading = 5, + Verifying = 6, + Finished = 7, + Seeding = 8, + Paused = 9, + Error = 10, + Unknown = 20 + + class DownloadStatus(dict): - def __init__(self, downloaded_bytes, total_bytes, download_speed, upload_speed): + def __init__(self, downloaded_bytes, total_bytes, download_speed, + upload_speed, torrent_status, progress, ratio): + """ + :type downloaded_bytes: int + :type total_bytes: int + :type download_speed: int + :type upload_speed: int + :type torrent_status: TorrentDownloadStatus + :type progress: float + :type ratio: float + """ self.downloaded_bytes = downloaded_bytes self.total_bytes = total_bytes self.download_speed = download_speed self.upload_speed = upload_speed + self.torrent_status = torrent_status + self.progress = progress + self.ratio = ratio + class TopicSettings(object): download_dir = None diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index 6822c492..03743283 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -9,7 +9,7 @@ from monitorrent.plugin_managers import register_plugin from datetime import datetime -from monitorrent.plugins.clients import DownloadStatus +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus log = structlog.get_logger() @@ -24,6 +24,23 @@ class DelugeCredentials(Base): password = Column(String, nullable=True) +status_mapping = { + "Queued": TorrentDownloadStatus.Queued, + "Downloading": TorrentDownloadStatus.Downloading, + "Seeding": TorrentDownloadStatus.Seeding, + "Paused": TorrentDownloadStatus.Paused, + "Checking": TorrentDownloadStatus.Checking, + "Error": TorrentDownloadStatus.Error, +} + + +def get_status(status, is_paused): + if is_paused: + return TorrentDownloadStatus.Paused + else: + return status_mapping[status] + + class DelugeClientPlugin(object): name = "deluge" form = [{ @@ -147,12 +164,15 @@ def get_download_status(self): return False client.connect() result = client.call("core.get_torrents_status", - {}, ['total_done', 'total_size', 'download_payload_rate', - 'upload_payload_rate', 'state', 'progress']) + {}, []) statuses = {} for key, value in result.items(): - statuses[key] = DownloadStatus(value[b'total_done'], value[b'total_size'], - value[b'download_payload_rate'], value[b'upload_payload_rate']) + statuses[key.decode("utf-8")] = DownloadStatus(value[b'total_done'], value[b'total_size'], + value[b'download_payload_rate'], + value[b'upload_payload_rate'], + get_status(value[b'state'].decode("utf-8"), + value[b'paused']), + value[b'progress'], value[b'ratio']) return statuses def get_download_status_by_hash(self, torrent_hash): @@ -166,7 +186,11 @@ def get_download_status_by_hash(self, torrent_hash): 'upload_payload_rate', 'state', 'progress']) key, value = result.popitem() return DownloadStatus(value[b'total_done'], value[b'total_size'], - value[b'download_payload_rate'], value[b'upload_payload_rate']) + value[b'download_payload_rate'], + value[b'upload_payload_rate'], + get_status(value[b'state'].decode("utf-8"), + value[b'paused']), + value[b'progress'], value[b'ratio']) register_plugin('client', 'deluge', DelugeClientPlugin()) diff --git a/monitorrent/plugins/clients/downloader.py b/monitorrent/plugins/clients/downloader.py index 69d26dde..c2ce7fc3 100644 --- a/monitorrent/plugins/clients/downloader.py +++ b/monitorrent/plugins/clients/downloader.py @@ -5,7 +5,7 @@ from sqlalchemy import Column, Integer, String from monitorrent.db import Base, DBSession from monitorrent.plugin_managers import register_plugin -from monitorrent.plugins.clients import DownloadStatus +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from monitorrent.utils.bittorrent_ex import Torrent import base64 @@ -110,9 +110,9 @@ def remove_torrent(self, torrent_hash): return False def get_download_status(self): - return {"": DownloadStatus(0, 0, 0, 0)} + return {"": DownloadStatus(0, 0, 0, 0, TorrentDownloadStatus.Unknown, 0, 0)} def get_download_status_by_hash(self, torrent_hash): - return DownloadStatus(0, 0, 0, 0) + return DownloadStatus(0, 0, 0, 0, TorrentDownloadStatus.Unknown, 0, 0) register_plugin('client', DownloaderPlugin.name, DownloaderPlugin()) diff --git a/monitorrent/plugins/clients/qbittorrent.py b/monitorrent/plugins/clients/qbittorrent.py index 29f0ac5a..1868bcee 100644 --- a/monitorrent/plugins/clients/qbittorrent.py +++ b/monitorrent/plugins/clients/qbittorrent.py @@ -17,7 +17,26 @@ from datetime import datetime import dateutil.parser -from monitorrent.plugins.clients import DownloadStatus +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus + +status_mapping = { + "queuedUP": TorrentDownloadStatus.Queued, + "queuedDL": TorrentDownloadStatus.Queued, + "stalledDL": TorrentDownloadStatus.Downloading, + "metaDL": TorrentDownloadStatus.Downloading, + "downloading": TorrentDownloadStatus.Downloading, + "stalledUP": TorrentDownloadStatus.Seeding, + "uploading": TorrentDownloadStatus.Seeding, + "pausedDL": TorrentDownloadStatus.Paused, + "pausedUP": TorrentDownloadStatus.Paused, + "checkingUP": TorrentDownloadStatus.Checking, + "checkingDL": TorrentDownloadStatus.Checking, + "error": TorrentDownloadStatus.Error, +} + + +def get_status(status): + return status_mapping[status] class QBittorrentCredentials(Base): @@ -180,7 +199,8 @@ def get_download_status(self): for torrent in result: torrents[torrent['hash']] = DownloadStatus(torrent['progress'] * torrent['size'], torrent['size'], torrent['dlspeed'], - torrent['upspeed']) + torrent['upspeed'], get_status(torrent['state']), + torrent['progress'], torrent['ratio']) return torrents def get_download_status_by_hash(self, torrent_hash): @@ -192,7 +212,8 @@ def get_download_status_by_hash(self, torrent_hash): response.raise_for_status() result = response.json() return DownloadStatus(result['total_downloaded'], result['total_size'], result['dl_speed'], - result['up_speed']) + result['up_speed'], TorrentDownloadStatus.Unknown, + result['total_downloaded'] / result['total_size'] * 100, result['share_ratio']) register_plugin('client', 'qbittorrent', QBittorrentClientPlugin()) diff --git a/monitorrent/plugins/clients/transmission.py b/monitorrent/plugins/clients/transmission.py index 770cb884..6f1c1c72 100644 --- a/monitorrent/plugins/clients/transmission.py +++ b/monitorrent/plugins/clients/transmission.py @@ -6,7 +6,19 @@ from monitorrent.plugin_managers import register_plugin import base64 -from monitorrent.plugins.clients import DownloadStatus +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus + +status_mapping = { + "stopped": TorrentDownloadStatus.Paused, + "check pending": TorrentDownloadStatus.Checking, + "checking": TorrentDownloadStatus.Checking, + "downloading": TorrentDownloadStatus.Downloading, + "seeding": TorrentDownloadStatus.Seeding +} + + +def get_status(status): + return status_mapping[status] class TransmissionCredentials(Base): @@ -121,22 +133,22 @@ def get_download_status(self): client = self.check_connection() if not client: return False - torrents = client.get_torrents(None, ['id', 'hashString', 'totalSize', 'downloadedEver', - 'rateDownload', 'rateUpload']) + torrents = client.get_torrents(None, []) result = {} for torrent in torrents: result[torrent.hashString] = DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, - torrent.rateUpload) + torrent.rateUpload, + get_status(torrent.status), torrent.progress, torrent.ratio) return result def get_download_status_by_hash(self, torrent_hash): client = self.check_connection() if not client: return False - torrent = client.get_torrent(torrent_hash.lower(), ['id', 'hashString', 'totalSize', 'downloadedEver', - 'rateDownload', 'rateUpload']) - return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload) + torrent = client.get_torrent(torrent_hash.lower(), []) + return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload, + get_status(torrent.status), torrent.progress, torrent.ratio) register_plugin('client', 'transmission', TransmissionClientPlugin()) diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index 721e839f..a8dd7a78 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import json +from enum import IntFlag import requests from io import BytesIO @@ -11,10 +12,37 @@ from monitorrent.db import Base, DBSession from monitorrent.plugin_managers import register_plugin -from monitorrent.plugins.clients import DownloadStatus +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from monitorrent.utils.soup import get_soup +class StatusFlags(IntFlag): + Started = 1, + Checking = 2, + StartAfterCheck = 4, + Checked = 8, + Error = 16, + Paused = 32, + Queued = 64, + Loaded = 128 + + +def get_status(status): + if StatusFlags.Error in status: + return TorrentDownloadStatus.Error + elif StatusFlags.Paused in status: + return TorrentDownloadStatus.Paused + elif StatusFlags.Started in status: + return TorrentDownloadStatus.Downloading + elif StatusFlags.Checking in status: + return TorrentDownloadStatus.Checking + elif StatusFlags.Queued in status: + return TorrentDownloadStatus.Queued + elif StatusFlags.Checked in status: + return TorrentDownloadStatus.Paused + + + class UTorrentCredentials(Base): __tablename__ = "utorrent_credentials" @@ -149,7 +177,9 @@ def get_download_status(self): array = json.loads(torrents.text)['torrents'] result = {} for torrent in array: - result[torrent[0]] = DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) + result[torrent[0]] = DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8], + get_status(StatusFlags(torrent[1])), + float(torrent[4]) / 10.0, torrent[7]) return result def get_download_status_by_hash(self, torrent_hash): @@ -163,7 +193,8 @@ def get_download_status_by_hash(self, torrent_hash): array = json.loads(torrents.text)['torrents'] torrent = next(torrent for torrent in array if torrent[0] == torrent_hash) if torrent: - return DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8]) + return DownloadStatus(torrent[5], torrent[3], torrent[9], torrent[8], get_status(StatusFlags(torrent[1])), + float(torrent[4]) / 10.0, torrent[7]) register_plugin('client', 'utorrent', UTorrentClientPlugin()) diff --git a/monitorrent/rest/notifiers.py b/monitorrent/rest/notifiers.py index db746eb7..0bae1f37 100644 --- a/monitorrent/rest/notifiers.py +++ b/monitorrent/rest/notifiers.py @@ -10,6 +10,7 @@ log = structlog.get_logger() + # noinspection PyUnusedLocal class NotifierCollection(object): def __init__(self, notifier_manager): @@ -21,10 +22,10 @@ def __init__(self, notifier_manager): def on_get(self, req, resp): try: resp.json = [{'name': name, - 'form': notifier.form, - 'enabled': self.notifier_manager.get_enabled(name), - 'has_settings': self.notifier_manager.get_settings(name) is not None} - for name, notifier in list(self.notifier_manager.notifiers.items())] + 'form': notifier.form, + 'enabled': self.notifier_manager.get_enabled(name), + 'has_settings': self.notifier_manager.get_settings(name) is not None} + for name, notifier in list(self.notifier_manager.notifiers.items())] except Exception as e: log.error("An error has occurred", exception=str(e)) raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py index 21a58ea0..007ddc90 100644 --- a/tests/plugins/clients/test_deluge_plugin.py +++ b/tests/plugins/clients/test_deluge_plugin.py @@ -286,7 +286,11 @@ def test_should_get_torrent_status_by_hash_successfully(self, deluge_client): rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30, b'total_size': 100, b'download_payload_rate': 50000.0, - b'upload_payload_rate': 0.0}} + b'upload_payload_rate': 0.0, + b'state': b'downloading', + b'progress': 23, + b'ratio': 1, + b'paused': True}} plugin = DelugeClientPlugin() settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} @@ -340,7 +344,12 @@ def test_should_get_torrent_statuses_successfully(self, deluge_client): rpc_client.call.return_value = {str.encode(torrent_hash.lower()): {b'total_done': 30, b'total_size': 100, b'download_payload_rate': 50000.0, - b'upload_payload_rate': 0.0}} + b'upload_payload_rate': 0.0, + b'state': b'Downloading', + b'progress': 23, + b'ratio': 1, + b'paused': False + }} plugin = DelugeClientPlugin() settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} @@ -378,5 +387,4 @@ def test_should_fail_get_torrent_status_on_error(self, deluge_client): plugin.get_download_status() rpc_client.call.assert_called_once_with("core.get_torrents_status", - {}, ['total_done', 'total_size', 'download_payload_rate', - 'upload_payload_rate', 'state', 'progress']) + {}, []) diff --git a/tests/plugins/clients/test_transmission_plugin.py b/tests/plugins/clients/test_transmission_plugin.py index 22ab121d..a8a87612 100644 --- a/tests/plugins/clients/test_transmission_plugin.py +++ b/tests/plugins/clients/test_transmission_plugin.py @@ -238,8 +238,9 @@ def test_get_download_dir_exception(self, transmission_client): def test_should_get_status_by_hash(self, transmission_client): rpc_client = transmission_client.return_value rpc_client.get_torrent.return_value = namedtuple('Torrent', - ('rateDownload', 'totalSize', 'downloadedEver', 'rateUpload') - )(5000, 10000, 6000, 20) + ('rateDownload', 'totalSize', 'downloadedEver', + 'rateUpload', 'status', 'progress', 'ratio') + )(5000, 10000, 6000, 20, 'downloading', 23, 1) plugin = TransmissionClientPlugin() settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} @@ -280,8 +281,8 @@ def test_should_get_status(self, transmission_client): torrent_id = '4e2597302ad6b4d7a545c8ec02621ac232316b96' rpc_client.get_torrents.return_value = [namedtuple('Torrent', ('hashString', 'rateDownload', 'totalSize', 'downloadedEver', - 'rateUpload') - )(torrent_id, 5000, 10000, 6000, 20)] + 'rateUpload', 'status', 'progress', 'ratio') + )(torrent_id, 5000, 10000, 6000, 20, 'downloading', 23, 1)] plugin = TransmissionClientPlugin() settings = {'host': 'localhost', 'username': 'monitorrent', 'password': 'monitorrent'} diff --git a/tests/rest/test_api_clients.py b/tests/rest/test_api_clients.py index 4fe9bfd6..8075a4d6 100644 --- a/tests/rest/test_api_clients.py +++ b/tests/rest/test_api_clients.py @@ -4,7 +4,7 @@ from mock import MagicMock from ddt import ddt, data -from monitorrent.plugins.clients import DownloadStatus +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from tests import RestTestBase from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus, \ ClientStatus @@ -114,7 +114,9 @@ def test_not_found_update_settings(self): class TorrentStatusTest(RestTestBase): def test_get_download_status_by_hash(self): clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) - clients_manager.get_download_status_by_id = MagicMock(return_value=DownloadStatus(1, 2, 3, 4)) + clients_manager.get_download_status_by_id = MagicMock(return_value=DownloadStatus(1, 2, 3, 4, + TorrentDownloadStatus.Unknown, + 5, 6)) client = TorrentStatus(clients_manager) client.__no_auth__ = True @@ -134,7 +136,9 @@ def test_get_download_status_by_hash(self): class ClientStatusTest(RestTestBase): def test_get_download_status(self): clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) - clients_manager.get_download_status = MagicMock(return_value={"torrent": DownloadStatus(1, 2, 3, 4)}) + clients_manager.get_download_status = MagicMock(return_value={"torrent": DownloadStatus(1, 2, 3, 4, + TorrentDownloadStatus + .Unknown, 5, 6)}) client = ClientStatus(clients_manager) client.__no_auth__ = True diff --git a/tests/test_plugin_managers_clients_manager.py b/tests/test_plugin_managers_clients_manager.py index 9f5ff646..0a9d57c5 100644 --- a/tests/test_plugin_managers_clients_manager.py +++ b/tests/test_plugin_managers_clients_manager.py @@ -1,7 +1,7 @@ from ddt import ddt, data from mock import Mock, MagicMock, patch -from monitorrent.plugins.clients import DownloadStatus +from monitorrent.plugins.clients import DownloadStatus, TorrentDownloadStatus from tests import TestCase, DbTestCase from monitorrent.plugin_managers import ClientsManager, DbClientsManager from monitorrent.settings_manager import SettingsManager @@ -176,7 +176,7 @@ def test_remove_torrent_false(self): remove_torrent_mock2.assert_not_called() def test_get_download_status_by_hash_success(self): - result = DownloadStatus(1, 2, 3, 4) + result = DownloadStatus(1, 2, 3, 4, TorrentDownloadStatus.Downloading, 5, 6) get_download_status_by_hash_1 = MagicMock(return_value=result) get_download_status_by_hash_2 = MagicMock(return_value=False) @@ -187,7 +187,7 @@ def test_get_download_status_by_hash_success(self): assert actual_result == result def test_get_download_status_success(self): - result = {"hash": DownloadStatus(1, 2, 3, 4)} + result = {"hash": DownloadStatus(1, 2, 3, 4, TorrentDownloadStatus.Downloading, 5, 6)} get_download_status = MagicMock(return_value=result) self.client1.get_download_status = get_download_status From 85b5d2a31d82d365d110b06c92178645cd07373c Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 2 Apr 2017 22:02:35 +0300 Subject: [PATCH 07/15] chore(tests): restored 100% coverage --- monitorrent/rest/clients.py | 15 ++------- monitorrent/rest/new_version.py | 6 +--- monitorrent/rest/notifiers.py | 23 +++---------- tests/rest/test_api_clients.py | 56 ++++++++++++++++++++++++++++++++ tests/rest/test_api_execute.py | 16 +++++++++ tests/rest/test_api_notifiers.py | 11 +++++++ 6 files changed, 92 insertions(+), 35 deletions(-) diff --git a/monitorrent/rest/clients.py b/monitorrent/rest/clients.py index ef18c884..e7944b1e 100644 --- a/monitorrent/rest/clients.py +++ b/monitorrent/rest/clients.py @@ -19,12 +19,8 @@ def __init__(self, clients_manager): self.clients_manager = clients_manager def on_get(self, req, resp): - try: - resp.json = [{'name': name, 'form': client.form, 'is_default': self.clients_manager.get_default() == client} - for name, client in list(self.clients_manager.clients.items())] - except Exception as e: - log.error("An error has occurred", exception=str(e)) - raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) + resp.json = [{'name': name, 'form': client.form, 'is_default': self.clients_manager.get_default() == client} + for name, client in list(self.clients_manager.clients.items())] # noinspection PyUnusedLocal @@ -73,14 +69,9 @@ def on_get(self, req, resp, torrent_hash): :type torrent_hash: str """ - try: - resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ - except Exception as e: - log.error("An error has occurred", exception=str(e)) - raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) + resp.json = self.clients_manager.get_download_status_by_id(torrent_hash).__dict__ # noinspection PyUnusedLocal -# noinspection PyUnusedLocal class ClientCheck(object): def __init__(self, clients_manager): """ diff --git a/monitorrent/rest/new_version.py b/monitorrent/rest/new_version.py index d9d10cec..70702593 100644 --- a/monitorrent/rest/new_version.py +++ b/monitorrent/rest/new_version.py @@ -15,8 +15,4 @@ def __init__(self, new_version_checker): self.new_version_checker = new_version_checker def on_get(self, req, resp): - try: - resp.json = {'url': self.new_version_checker.new_version_url} - except Exception as e: - log.error("An error has occurred", exception=str(e)) - raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) + resp.json = {'url': self.new_version_checker.new_version_url} diff --git a/monitorrent/rest/notifiers.py b/monitorrent/rest/notifiers.py index 0bae1f37..7dc6e3bd 100644 --- a/monitorrent/rest/notifiers.py +++ b/monitorrent/rest/notifiers.py @@ -20,15 +20,11 @@ def __init__(self, notifier_manager): self.notifier_manager = notifier_manager def on_get(self, req, resp): - try: - resp.json = [{'name': name, - 'form': notifier.form, - 'enabled': self.notifier_manager.get_enabled(name), - 'has_settings': self.notifier_manager.get_settings(name) is not None} - for name, notifier in list(self.notifier_manager.notifiers.items())] - except Exception as e: - log.error("An error has occurred", exception=str(e)) - raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) + resp.json = [{'name': name, + 'form': notifier.form, + 'enabled': self.notifier_manager.get_enabled(name), + 'has_settings': self.notifier_manager.get_settings(name) is not None} + for name, notifier in list(self.notifier_manager.notifiers.items())] # noinspection PyUnusedLocal @@ -48,9 +44,6 @@ def on_get(self, req, resp, notifier): except KeyError as e: log.error("Notifier could not be found", notifier=notifier, exception=str(e)) raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) - except Exception as e: - log.error("An error has occurred", exception=str(e)) - raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) resp.json = result.__props__() def on_put(self, req, resp, notifier): @@ -60,9 +53,6 @@ def on_put(self, req, resp, notifier): except KeyError as e: log.error("Notifier could not be found", notifier=notifier, exception=str(e)) raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) - except Exception as e: - log.error("An error has occurred", exception=str(e)) - raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) if not updated: log.error("Notifier plugin doesn't support settings", notifier=notifier) raise falcon.HTTPBadRequest('NotSettable', 'Notifier plugin \'{0}\' doesn\'t support settings' @@ -105,9 +95,6 @@ def on_put(self, req, resp, notifier): except KeyError as e: log.error("Notifier could not be found", notifier=notifier, exception=str(e)) raise falcon.HTTPNotFound(title='Notifier plugin \'{0}\' not found'.format(notifier), description=str(e)) - except Exception as e: - log.error("An error has occurred", exception=str(e)) - raise falcon.HTTPInternalServerError(title='A server has encountered an error', description=str(e)) if not updated: log.error("Notifier plugin doesn't support settings", notifier=notifier) raise falcon.HTTPBadRequest('NotSettable', 'Notifier plugin \'{0}\' doesn\'t support settings' diff --git a/tests/rest/test_api_clients.py b/tests/rest/test_api_clients.py index 8075a4d6..419354f0 100644 --- a/tests/rest/test_api_clients.py +++ b/tests/rest/test_api_clients.py @@ -87,6 +87,18 @@ def test_not_found_settings(self): self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type']) + def test_error_get_settings(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_settings = MagicMock(side_effect=Exception) + + client = Client(clients_manager) + self.api.add_route('/api/clients/{client}', client) + + self.simulate_request('/api/clients/{0}'.format(1)) + + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) + self.assertTrue('application/json' in self.srmock.headers_dict['Content-Type']) + def test_successful_update_settings(self): clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) clients_manager.set_settings = MagicMock(return_value=True) @@ -109,6 +121,17 @@ def test_not_found_update_settings(self): body=json.dumps({'login': 'login', 'password': 'password'})) self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + def test_error_update_settings(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.set_settings = MagicMock(side_effect=Exception) + + client = Client(clients_manager) + self.api.add_route('/api/clients/{client}', client) + + self.simulate_request('/api/clients/{0}'.format(1), method="PUT", + body=json.dumps({'login': 'login', 'password': 'password'})) + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) + @ddt class TorrentStatusTest(RestTestBase): @@ -166,6 +189,17 @@ def test_check_client_not_found(self): self.simulate_request('/api/clients/{0}/status'.format("client")) self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + def test_check_client_error(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.get_download_status = MagicMock(side_effect=Exception) + + client = ClientStatus(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/status', client) + + self.simulate_request('/api/clients/{0}/status'.format("client")) + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) + @ddt class CheckClientTest(RestTestBase): @@ -198,6 +232,17 @@ def test_check_client_not_found(self): self.simulate_request('/api/clients/{0}/check'.format('tracker.org')) self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + def test_check_client_error(self): + clients_manager = ClientsManager({'test': ClientCollectionTest.TestClient()}) + clients_manager.check_connection = MagicMock(side_effect=Exception) + + client = ClientCheck(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/check', client) + + self.simulate_request('/api/clients/{0}/check'.format('tracker.org')) + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) + class DefaultClientTest(RestTestBase): class TestClientWithFields(object): @@ -275,3 +320,14 @@ def test_set_default_not_found(self): self.simulate_request('/api/clients/{0}/default'.format('random.org'), method='PUT') self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + + def test_set_default_on_error(self): + clients_manager = ClientsManager({'tracker.org': ClientCollectionTest.TestClient()}) + clients_manager.set_default = MagicMock(side_effect=Exception) + + client = ClientDefault(clients_manager) + client.__no_auth__ = True + self.api.add_route('/api/clients/{client}/default', client) + + self.simulate_request('/api/clients/{0}/default'.format('tracker.org'), method='PUT') + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) diff --git a/tests/rest/test_api_execute.py b/tests/rest/test_api_execute.py index 5d4c285f..c56d1370 100644 --- a/tests/rest/test_api_execute.py +++ b/tests/rest/test_api_execute.py @@ -99,6 +99,22 @@ def set_result(): self.assertEqual(result, {'is_running': True, 'logs': [{}]}) + def test_wrong_params(self): + log_manager = ExecuteLogManager() + log_manager.get_current_execute_log_details = Mock(side_effect=Exception) + log_manager.is_running = Mock(return_value=False) + time = TimeMock() + + with patch("monitorrent.rest.execute.time", time): + + execute_log_current = ExecuteLogCurrent(log_manager) + + self.api.add_route(self.test_route, execute_log_current) + self.simulate_request(self.test_route, decode='utf-8') + + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) + + @ddt class ExecuteCallTest(RestTestBase): diff --git a/tests/rest/test_api_notifiers.py b/tests/rest/test_api_notifiers.py index 87960a01..54a65b13 100644 --- a/tests/rest/test_api_notifiers.py +++ b/tests/rest/test_api_notifiers.py @@ -217,3 +217,14 @@ def test_check_notifier_not_found(self): self.simulate_request('/api/clients/{0}/check'.format('tracker.org')) self.assertEqual(self.srmock.status, falcon.HTTP_NOT_FOUND) + + def test_check_notifier_exception(self): + notifiers_manager = NotifierManager(Mock(), {'test': NotifierCollectionTest.TestNotifier()}) + notifiers_manager.send_test_message = Mock(side_effect=Exception) + + notifier = NotifierCheck(notifiers_manager) + notifier.__no_auth__ = True + self.api.add_route('/api/clients/{notifier}/check', notifier) + + self.simulate_request('/api/clients/{0}/check'.format('tracker.org')) + self.assertEqual(self.srmock.status, falcon.HTTP_INTERNAL_SERVER_ERROR) From 5d11a8c0be347b882a662e4d024bdbebe3e16f54 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 2 Apr 2017 22:19:48 +0300 Subject: [PATCH 08/15] fix(plugins): replaced enum package with more advanced one --- monitorrent/plugins/clients/__init__.py | 2 +- monitorrent/plugins/notifiers/__init__.py | 2 +- monitorrent/plugins/status.py | 2 +- monitorrent/plugins/trackers/__init__.py | 2 +- monitorrent/plugins/trackers/lostfilm.py | 2 +- monitorrent/rest/__init__.py | 2 +- monitorrent/settings_manager.py | 2 +- requirements.txt | 2 +- tests/rest/test_api.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py index 62d4f939..d6df1d4e 100644 --- a/monitorrent/plugins/clients/__init__.py +++ b/monitorrent/plugins/clients/__init__.py @@ -1,4 +1,4 @@ -from enum import Enum +from aenum import Enum from monitorrent.plugins.trackers import Topic diff --git a/monitorrent/plugins/notifiers/__init__.py b/monitorrent/plugins/notifiers/__init__.py index c054fc44..c527c24a 100644 --- a/monitorrent/plugins/notifiers/__init__.py +++ b/monitorrent/plugins/notifiers/__init__.py @@ -3,7 +3,7 @@ import inspect from abc import ABCMeta, abstractproperty, abstractmethod -from enum import Enum +from aenum import Enum from sqlalchemy import String, Column, Integer, Boolean from monitorrent.db import DBSession, dict2row, row2dict, Base diff --git a/monitorrent/plugins/status.py b/monitorrent/plugins/status.py index 6140485b..bf3c205d 100644 --- a/monitorrent/plugins/status.py +++ b/monitorrent/plugins/status.py @@ -1,4 +1,4 @@ -from enum import Enum +from aenum import Enum class Status(Enum): diff --git a/monitorrent/plugins/trackers/__init__.py b/monitorrent/plugins/trackers/__init__.py index cdd9bda2..3f080be7 100644 --- a/monitorrent/plugins/trackers/__init__.py +++ b/monitorrent/plugins/trackers/__init__.py @@ -2,7 +2,7 @@ import html import six import pprint -from enum import Enum +from aenum import Enum from monitorrent.db import DBSession, row2dict, dict2row from monitorrent.plugins import Topic from monitorrent.plugins.status import Status diff --git a/monitorrent/plugins/trackers/lostfilm.py b/monitorrent/plugins/trackers/lostfilm.py index 81091c28..3ed545ff 100644 --- a/monitorrent/plugins/trackers/lostfilm.py +++ b/monitorrent/plugins/trackers/lostfilm.py @@ -4,7 +4,7 @@ import requests import traceback import six -from enum import Enum +from aenum import Enum from requests import Response from sqlalchemy import Column, Integer, String, MetaData, Table, ForeignKey from monitorrent.db import Base, DBSession, UTCDateTime, row2dict diff --git a/monitorrent/rest/__init__.py b/monitorrent/rest/__init__.py index f2ca30b9..8bec0c75 100644 --- a/monitorrent/rest/__init__.py +++ b/monitorrent/rest/__init__.py @@ -4,7 +4,7 @@ import json import datetime import falcon -from enum import Enum +from aenum import Enum from itsdangerous import JSONWebSignatureSerializer, BadSignature diff --git a/monitorrent/settings_manager.py b/monitorrent/settings_manager.py index f1e00928..ef0258f1 100644 --- a/monitorrent/settings_manager.py +++ b/monitorrent/settings_manager.py @@ -1,6 +1,6 @@ from builtins import str from builtins import object -from enum import Enum +from aenum import Enum from sqlalchemy import Column, Integer, String from monitorrent.db import DBSession, Base diff --git a/requirements.txt b/requirements.txt index afd7b4e6..7099ee0e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,5 +18,5 @@ structlog #python 2/3 compatibility and backports six -enum34>=1.0.4 +aenum>=2.0.6 future diff --git a/tests/rest/test_api.py b/tests/rest/test_api.py index 47189208..a0ee3a84 100644 --- a/tests/rest/test_api.py +++ b/tests/rest/test_api.py @@ -1,7 +1,7 @@ from builtins import str from builtins import object import falcon -from enum import Enum +from aenum import Enum from datetime import datetime from monitorrent.rest import MonitorrentJSONEncoder from unittest import TestCase From 89936ef7587d40ba0fb3cab03ac04d4701f5a8dc Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 30 Apr 2017 19:27:40 +0300 Subject: [PATCH 09/15] fix(test) postmerge fixes --- monitorrent/plugins/clients/deluge.py | 1 - monitorrent/plugins/clients/qbittorrent.py | 3 --- monitorrent/plugins/clients/transmission.py | 3 --- monitorrent/plugins/clients/utorrent.py | 4 ---- tests/plugins/clients/test_deluge_plugin.py | 3 ++- tests/plugins/clients/test_qbittorrent.py | 6 ++++-- tests/plugins/clients/test_transmission_plugin.py | 6 ++++-- tests/plugins/clients/test_utorrent_plugin.py | 6 ++++-- 8 files changed, 14 insertions(+), 18 deletions(-) diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index 1117c707..a9023b1b 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -160,7 +160,6 @@ def remove_torrent(self, torrent_hash): def get_download_status(self): client = self._get_client() - if not client: client.connect() result = client.call("core.get_torrents_status", {}, []) diff --git a/monitorrent/plugins/clients/qbittorrent.py b/monitorrent/plugins/clients/qbittorrent.py index b60f41ac..6a006ea8 100644 --- a/monitorrent/plugins/clients/qbittorrent.py +++ b/monitorrent/plugins/clients/qbittorrent.py @@ -190,7 +190,6 @@ def remove_torrent(self, torrent_hash): def get_download_status(self): parameters = self._get_params() - if not parameters: response = parameters['session'].get(parameters['target'] + "query/torrents/") response.raise_for_status() result = response.json() @@ -204,8 +203,6 @@ def get_download_status(self): def get_download_status_by_hash(self, torrent_hash): parameters = self._get_params() - if not parameters: - return False torrent_hash = torrent_hash.lower() response = parameters['session'].get(parameters['target'] + "query/propertiesGeneral/" + torrent_hash) response.raise_for_status() diff --git a/monitorrent/plugins/clients/transmission.py b/monitorrent/plugins/clients/transmission.py index 22c42b45..fb2fe071 100644 --- a/monitorrent/plugins/clients/transmission.py +++ b/monitorrent/plugins/clients/transmission.py @@ -131,7 +131,6 @@ def remove_torrent(self, torrent_hash): def get_download_status(self): client = self.check_connection() - if not client: torrents = client.get_torrents(None, []) result = {} for torrent in torrents: @@ -143,8 +142,6 @@ def get_download_status(self): def get_download_status_by_hash(self, torrent_hash): client = self.check_connection() - if not client: - return False torrent = client.get_torrent(torrent_hash.lower(), []) return DownloadStatus(torrent.downloadedEver, torrent.totalSize, torrent.rateDownload, torrent.rateUpload, get_status(torrent.status), torrent.progress, torrent.ratio) diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index 5559a080..62b76f28 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -168,8 +168,6 @@ def remove_torrent(self, torrent_hash): def get_download_status(self): parameters = self._get_params() - if not parameters: - payload = {"list": '1', "token": parameters["token"]} torrents = parameters['session'].get(parameters['target'], params=payload) @@ -183,8 +181,6 @@ def get_download_status(self): def get_download_status_by_hash(self, torrent_hash): parameters = self._get_params() - if not parameters: - return False payload = {"list": '1', "token": parameters["token"]} torrents = parameters['session'].get(parameters['target'], diff --git a/tests/plugins/clients/test_deluge_plugin.py b/tests/plugins/clients/test_deluge_plugin.py index 007ddc90..d3921e7c 100644 --- a/tests/plugins/clients/test_deluge_plugin.py +++ b/tests/plugins/clients/test_deluge_plugin.py @@ -369,7 +369,8 @@ def test_should_fail_when_no_settings_on_get_torrent_statuses(self, deluge_clien plugin = DelugeClientPlugin() - assert plugin.get_download_status() is False + with pytest.raises(Exception): + plugin.get_download_status() rpc_client.call.assert_not_called() diff --git a/tests/plugins/clients/test_qbittorrent.py b/tests/plugins/clients/test_qbittorrent.py index 82066a85..7e622328 100644 --- a/tests/plugins/clients/test_qbittorrent.py +++ b/tests/plugins/clients/test_qbittorrent.py @@ -304,7 +304,8 @@ def test_should_fail_download_status_by_hash_when_not_logged_in(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - assert plugin.get_download_status_by_hash(hash_string) is False + with pytest.raises(Exception): + plugin.get_download_status_by_hash(hash_string) @Mocker() def test_should_fail_when_download_status_by_hash_wasnt_retrieved(self, mocker): @@ -374,7 +375,8 @@ def test_should_fail_download_status_when_not_logged_in(self, mocker): plugin = QBittorrentClientPlugin() plugin.set_settings(settings) - assert plugin.get_download_status() is False + with pytest.raises(Exception): + plugin.get_download_status() @Mocker() def test_should_fail_when_download_status_wasnt_retrieved(self, mocker): diff --git a/tests/plugins/clients/test_transmission_plugin.py b/tests/plugins/clients/test_transmission_plugin.py index a8a87612..a35233ac 100644 --- a/tests/plugins/clients/test_transmission_plugin.py +++ b/tests/plugins/clients/test_transmission_plugin.py @@ -258,7 +258,8 @@ def test_should_fail_when_no_settings_for_get_status_by_hash(self, transmission_ rpc_client.get_torrent.side_effect = transmissionrpc.TransmissionError plugin = TransmissionClientPlugin() - assert plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') == False + with pytest.raises(Exception): + plugin.get_download_status_by_hash('4e2597302ad6b4d7a545c8ec02621ac232316b96') rpc_client.get_torrent.assert_not_called() @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') @@ -303,7 +304,8 @@ def test_should_fail_when_no_settings_for_get_status(self, transmission_client): plugin = TransmissionClientPlugin() - assert plugin.get_download_status() is False + with pytest.raises(Exception): + plugin.get_download_status() rpc_client.get_torrent.assert_not_called() @patch('monitorrent.plugins.clients.transmission.transmissionrpc.Client') diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py index 2ca66640..6ad7b856 100644 --- a/tests/plugins/clients/test_utorrent_plugin.py +++ b/tests/plugins/clients/test_utorrent_plugin.py @@ -184,7 +184,8 @@ def test_get_download_status_by_hash_success(self): def test_get_download_status_by_hash_bad_settings(self): plugin = UTorrentClientPlugin() torrent = 'torrent' - assert plugin.get_download_status_by_hash(torrent) is False + with pytest.raises(TypeError): + plugin.get_download_status_by_hash(torrent) @use_vcr def test_get_download_status_by_hash_not_found(self): @@ -216,7 +217,8 @@ def test_get_download_status_success(self): def test_get_download_status_bad_settings(self): plugin = UTorrentClientPlugin() - assert plugin.get_download_status() is False + with pytest.raises(TypeError): + plugin.get_download_status() @use_vcr def test_get_download_status_not_found(self): From c536159a68dbe066b7011f4178e2163f4ed0fd18 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 30 Apr 2017 19:35:25 +0300 Subject: [PATCH 10/15] fix(plugins/clients/utorrent): incorrect import --- monitorrent/plugins/clients/utorrent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index 62b76f28..c4ae7253 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import json -from enum import IntFlag +from aenum import IntFlag import requests from io import BytesIO From c740d68ff900a7480e467b7eef61d36e446edee2 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 30 Apr 2017 19:40:44 +0300 Subject: [PATCH 11/15] fix(engine): some code comments fixes. --- monitorrent/engine.py | 2 +- monitorrent/plugin_managers.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/monitorrent/engine.py b/monitorrent/engine.py index 738e2c9a..84943c40 100644 --- a/monitorrent/engine.py +++ b/monitorrent/engine.py @@ -381,7 +381,7 @@ def info(self, message): def failed(self, message, exc_type=None, exc_value=None, exc_tb=None): if exc_value is not None: formatted_exception = u''.join(traceback.format_exception(exc_type, exc_value, exc_tb)) - failed_message = u'{0}
{1}
' \ + failed_message = u'{0}
{1}
'\ .format(message, html.escape(formatted_exception).replace(u'\n', u'
')) else: failed_message = message diff --git a/monitorrent/plugin_managers.py b/monitorrent/plugin_managers.py index 2db4cd8a..7909348e 100644 --- a/monitorrent/plugin_managers.py +++ b/monitorrent/plugin_managers.py @@ -183,7 +183,6 @@ def __init__(self, clients=None, default_client_name=None): list(self.clients.values())[0] if len(self.clients) > 0 else None) def set_default(self, name): - default_client = self.__get_default_client(name) if default_client is None: raise KeyError() From ea228e6bf3fa4e7efc044ab204ce68e41a323558 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Sun, 30 Apr 2017 19:49:46 +0300 Subject: [PATCH 12/15] feat(test): added tests for utorrent get_status method --- tests/plugins/clients/test_utorrent_plugin.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py index 6ad7b856..c338932e 100644 --- a/tests/plugins/clients/test_utorrent_plugin.py +++ b/tests/plugins/clients/test_utorrent_plugin.py @@ -4,7 +4,8 @@ from mock import patch, Mock, MagicMock from requests import Response -from monitorrent.plugins.clients.utorrent import UTorrentClientPlugin +from monitorrent.plugins.clients import TorrentDownloadStatus +from monitorrent.plugins.clients.utorrent import UTorrentClientPlugin, StatusFlags, get_status from tests import DbTestCase, use_vcr @@ -229,3 +230,15 @@ def test_get_download_status_not_found(self): with pytest.raises(Exception) as e: plugin.get_download_status() + + def test_get_status(self): + status = StatusFlags.Error + assert get_status(status) == TorrentDownloadStatus.Error + status = StatusFlags.Paused + assert get_status(status) == TorrentDownloadStatus.Paused + status = StatusFlags.Checking + assert get_status(status) == TorrentDownloadStatus.Checking + status = StatusFlags.Started + assert get_status(status) == TorrentDownloadStatus.Downloading + status = StatusFlags.Queued + assert get_status(status) == TorrentDownloadStatus.Queued From b9c80e43ff0def48806cc470dbfedb41525829ed Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Mon, 1 May 2017 19:25:31 +0300 Subject: [PATCH 13/15] fix(test): revetrted package change for enums, causing problems with p2 --- monitorrent/plugins/clients/__init__.py | 2 +- monitorrent/plugins/clients/utorrent.py | 1 + monitorrent/plugins/notifiers/__init__.py | 2 +- monitorrent/plugins/status.py | 2 +- monitorrent/plugins/trackers/__init__.py | 2 +- monitorrent/plugins/trackers/lostfilm.py | 2 +- monitorrent/rest/__init__.py | 2 +- monitorrent/settings_manager.py | 2 +- requirements.txt | 3 ++- tests/rest/test_api.py | 2 +- 10 files changed, 11 insertions(+), 9 deletions(-) diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py index d6df1d4e..62d4f939 100644 --- a/monitorrent/plugins/clients/__init__.py +++ b/monitorrent/plugins/clients/__init__.py @@ -1,4 +1,4 @@ -from aenum import Enum +from enum import Enum from monitorrent.plugins.trackers import Topic diff --git a/monitorrent/plugins/clients/utorrent.py b/monitorrent/plugins/clients/utorrent.py index c4ae7253..421cd4c1 100644 --- a/monitorrent/plugins/clients/utorrent.py +++ b/monitorrent/plugins/clients/utorrent.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import json + from aenum import IntFlag import requests diff --git a/monitorrent/plugins/notifiers/__init__.py b/monitorrent/plugins/notifiers/__init__.py index c527c24a..c054fc44 100644 --- a/monitorrent/plugins/notifiers/__init__.py +++ b/monitorrent/plugins/notifiers/__init__.py @@ -3,7 +3,7 @@ import inspect from abc import ABCMeta, abstractproperty, abstractmethod -from aenum import Enum +from enum import Enum from sqlalchemy import String, Column, Integer, Boolean from monitorrent.db import DBSession, dict2row, row2dict, Base diff --git a/monitorrent/plugins/status.py b/monitorrent/plugins/status.py index bf3c205d..6140485b 100644 --- a/monitorrent/plugins/status.py +++ b/monitorrent/plugins/status.py @@ -1,4 +1,4 @@ -from aenum import Enum +from enum import Enum class Status(Enum): diff --git a/monitorrent/plugins/trackers/__init__.py b/monitorrent/plugins/trackers/__init__.py index 3f080be7..cdd9bda2 100644 --- a/monitorrent/plugins/trackers/__init__.py +++ b/monitorrent/plugins/trackers/__init__.py @@ -2,7 +2,7 @@ import html import six import pprint -from aenum import Enum +from enum import Enum from monitorrent.db import DBSession, row2dict, dict2row from monitorrent.plugins import Topic from monitorrent.plugins.status import Status diff --git a/monitorrent/plugins/trackers/lostfilm.py b/monitorrent/plugins/trackers/lostfilm.py index 3ed545ff..81091c28 100644 --- a/monitorrent/plugins/trackers/lostfilm.py +++ b/monitorrent/plugins/trackers/lostfilm.py @@ -4,7 +4,7 @@ import requests import traceback import six -from aenum import Enum +from enum import Enum from requests import Response from sqlalchemy import Column, Integer, String, MetaData, Table, ForeignKey from monitorrent.db import Base, DBSession, UTCDateTime, row2dict diff --git a/monitorrent/rest/__init__.py b/monitorrent/rest/__init__.py index 8bec0c75..f2ca30b9 100644 --- a/monitorrent/rest/__init__.py +++ b/monitorrent/rest/__init__.py @@ -4,7 +4,7 @@ import json import datetime import falcon -from aenum import Enum +from enum import Enum from itsdangerous import JSONWebSignatureSerializer, BadSignature diff --git a/monitorrent/settings_manager.py b/monitorrent/settings_manager.py index ef0258f1..f1e00928 100644 --- a/monitorrent/settings_manager.py +++ b/monitorrent/settings_manager.py @@ -1,6 +1,6 @@ from builtins import str from builtins import object -from aenum import Enum +from enum import Enum from sqlalchemy import Column, Integer, String from monitorrent.db import DBSession, Base diff --git a/requirements.txt b/requirements.txt index 7099ee0e..84de59ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,5 +18,6 @@ structlog #python 2/3 compatibility and backports six -aenum>=2.0.6 +enum34 +aenum future diff --git a/tests/rest/test_api.py b/tests/rest/test_api.py index a0ee3a84..47189208 100644 --- a/tests/rest/test_api.py +++ b/tests/rest/test_api.py @@ -1,7 +1,7 @@ from builtins import str from builtins import object import falcon -from aenum import Enum +from enum import Enum from datetime import datetime from monitorrent.rest import MonitorrentJSONEncoder from unittest import TestCase From 741193d6d65b84fc2173d8ce2e9ad94ee964c44e Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Mon, 1 May 2017 19:41:55 +0300 Subject: [PATCH 14/15] chore(tests): restored coverage --- tests/plugins/clients/test_utorrent_plugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/plugins/clients/test_utorrent_plugin.py b/tests/plugins/clients/test_utorrent_plugin.py index c338932e..f852ee18 100644 --- a/tests/plugins/clients/test_utorrent_plugin.py +++ b/tests/plugins/clients/test_utorrent_plugin.py @@ -242,3 +242,5 @@ def test_get_status(self): assert get_status(status) == TorrentDownloadStatus.Downloading status = StatusFlags.Queued assert get_status(status) == TorrentDownloadStatus.Queued + status = StatusFlags.Checked + assert get_status(status) == TorrentDownloadStatus.Paused From 44d1f2c1a7f2650e4b17e5a160c2c0ba3d838825 Mon Sep 17 00:00:00 2001 From: Dzmitry Safarau Date: Mon, 1 May 2017 20:19:30 +0300 Subject: [PATCH 15/15] chore(test): code quality improvements --- monitorrent/plugins/clients/__init__.py | 5 +++-- monitorrent/plugins/clients/deluge.py | 3 +-- server.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/monitorrent/plugins/clients/__init__.py b/monitorrent/plugins/clients/__init__.py index 62d4f939..8131417b 100644 --- a/monitorrent/plugins/clients/__init__.py +++ b/monitorrent/plugins/clients/__init__.py @@ -17,8 +17,7 @@ class TorrentDownloadStatus(Enum): class DownloadStatus(dict): - def __init__(self, downloaded_bytes, total_bytes, download_speed, - upload_speed, torrent_status, progress, ratio): + def __init__(self, downloaded_bytes, total_bytes, download_speed, upload_speed, torrent_status, progress, ratio): """ :type downloaded_bytes: int :type total_bytes: int @@ -28,6 +27,8 @@ def __init__(self, downloaded_bytes, total_bytes, download_speed, :type progress: float :type ratio: float """ + + super(DownloadStatus, self).__init__() self.downloaded_bytes = downloaded_bytes self.total_bytes = total_bytes self.download_speed = download_speed diff --git a/monitorrent/plugins/clients/deluge.py b/monitorrent/plugins/clients/deluge.py index a9023b1b..f59697c8 100644 --- a/monitorrent/plugins/clients/deluge.py +++ b/monitorrent/plugins/clients/deluge.py @@ -1,4 +1,3 @@ -import six import base64 import structlog @@ -182,7 +181,7 @@ def get_download_status_by_hash(self, torrent_hash): result = client.call("core.get_torrents_status", {'hash': lower_hash}, ['total_done', 'total_size', 'download_payload_rate', 'upload_payload_rate', 'state', 'progress']) - key, value = result.popitem() + value = result.popitem()[1] return DownloadStatus(value[b'total_done'], value[b'total_size'], value[b'download_payload_rate'], value[b'upload_payload_rate'], diff --git a/server.py b/server.py index 07b9ddf2..4990d707 100644 --- a/server.py +++ b/server.py @@ -25,8 +25,8 @@ from monitorrent.rest.login import Login, Logout from monitorrent.rest.topics import TopicCollection, TopicParse, Topic, TopicResetStatus, TopicPauseState from monitorrent.rest.trackers import TrackerCollection, Tracker, TrackerCheck -from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, TorrentStatus, \ - ClientStatus +from monitorrent.rest.clients import ClientCollection, Client, ClientCheck, DefaultClient, ClientDefault, \ + TorrentStatus, ClientStatus from monitorrent.rest.settings_authentication import SettingsAuthentication from monitorrent.rest.settings_password import SettingsPassword from monitorrent.rest.settings_execute import SettingsExecute