summaryrefslogtreecommitdiffstats
path: root/deluge/plugins/Stats/deluge_stats/core.py
diff options
context:
space:
mode:
authorCalum Lind <calumlind+deluge@gmail.com>2019-05-14 14:41:58 +0100
committerCalum Lind <calumlind+deluge@gmail.com>2019-05-15 19:20:08 +0100
commit535b13b5f1b7b7d3d104da14c4cb2a4a9502bfe0 (patch)
treefa43cf0e74dd14bc5d49dd1980997b1ae36778ea /deluge/plugins/Stats/deluge_stats/core.py
parentd6a0276a78ff99f1a0ee7faf421e048d5edbf693 (diff)
downloaddeluge-535b13b5f1b7b7d3d104da14c4cb2a4a9502bfe0.tar.gz
deluge-535b13b5f1b7b7d3d104da14c4cb2a4a9502bfe0.tar.bz2
deluge-535b13b5f1b7b7d3d104da14c4cb2a4a9502bfe0.zip
[Plugins] Convert plugins to `deluge_` module prefix convention
This commit reverts namespace for the plugins and uses a module prefix "deluge_" in it's place. The distribution package name remains the same for now but will also be considered to use a prefix to help find the third-party plugins e.g. Deluge-{Plugin} and the pluginmanager will strip the prefix for displaying. The change is a result of problems trying to package Deluge with pyinstaller and the pkg_resources namespaces is not compatible. Testing alternatives to using the pkgutil or PEP420 (native) namespaces did not yield any joy either as importing eggs with namespaces does not work. [1] At this point importable eggs are considered deprecated but there is no viable alternative yet. [2] [1] https://github.com/pypa/packaging-problems/issues/212 [2] https://github.com/pypa/packaging-problems/issues/244
Diffstat (limited to 'deluge/plugins/Stats/deluge_stats/core.py')
-rw-r--r--deluge/plugins/Stats/deluge_stats/core.py223
1 files changed, 223 insertions, 0 deletions
diff --git a/deluge/plugins/Stats/deluge_stats/core.py b/deluge/plugins/Stats/deluge_stats/core.py
new file mode 100644
index 000000000..635c54db6
--- /dev/null
+++ b/deluge/plugins/Stats/deluge_stats/core.py
@@ -0,0 +1,223 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
+# Copyright (C) 2008 Damien Churchill <damoxc@gmail.com>
+# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
+# Copyright (C) 2007 Marcos Mobley <markybob@gmail.com>
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+
+from __future__ import division, unicode_literals
+
+import logging
+import time
+
+from twisted.internet.task import LoopingCall
+
+from deluge import component, configmanager
+from deluge.core.rpcserver import export
+from deluge.plugins.pluginbase import CorePluginBase
+
+DEFAULT_PREFS = {
+ 'test': 'NiNiNi',
+ 'update_interval': 1, # 2 seconds.
+ 'length': 150, # 2 seconds * 150 --> 5 minutes.
+}
+
+DEFAULT_TOTALS = {
+ 'total_upload': 0,
+ 'total_download': 0,
+ 'total_payload_upload': 0,
+ 'total_payload_download': 0,
+ 'stats': {},
+}
+
+log = logging.getLogger(__name__)
+
+
+def get_key(config, key):
+ try:
+ return config[key]
+ except KeyError:
+ return None
+
+
+def mean(items):
+ try:
+ return sum(items) // len(items)
+ except Exception:
+ return 0
+
+
+class Core(CorePluginBase):
+ totals = {} # class var to catch only updating this once per session in enable.
+
+ def enable(self):
+ log.debug('Stats plugin enabled')
+ self.core = component.get('Core')
+ self.stats = {}
+ self.count = {}
+ self.intervals = [1, 5, 30, 300]
+
+ self.last_update = {}
+ t = time.time()
+ for i in self.intervals:
+ self.stats[i] = {}
+ self.last_update[i] = t
+ self.count[i] = 0
+
+ self.config = configmanager.ConfigManager('stats.conf', DEFAULT_PREFS)
+ self.saved_stats = configmanager.ConfigManager('stats.totals', DEFAULT_TOTALS)
+ if self.totals == {}:
+ self.totals.update(self.saved_stats.config)
+
+ self.length = self.config['length']
+
+ # self.stats = get_key(self.saved_stats, 'stats') or {}
+ self.stats_keys = ['peer.num_peers_half_open', 'dht.dht_node_cache']
+ self.add_stats(
+ 'upload_rate',
+ 'download_rate',
+ 'dht_nodes',
+ 'dht_cache_nodes',
+ 'dht_torrents',
+ 'num_peers',
+ 'num_connections',
+ )
+
+ self.update_stats()
+
+ self.update_timer = LoopingCall(self.update_stats)
+ self.update_timer.start(self.config['update_interval'])
+
+ self.save_timer = LoopingCall(self.save_stats)
+ self.save_timer.start(60)
+
+ def disable(self):
+ self.update_timer.stop() if self.update_timer.running else None
+ self.save_timer.stop() if self.save_timer.running else None
+ self.save_stats()
+
+ def add_stats(self, *stats):
+ for stat in stats:
+ if stat not in self.stats_keys:
+ self.stats_keys.append(stat)
+ for i in self.intervals:
+ if stat not in self.stats[i]:
+ self.stats[i][stat] = []
+
+ def update_stats(self):
+ # Get all possible stats!
+ stats = {}
+ for key in self.stats_keys:
+ # try all keys we have, very inefficient but saves having to
+ # work out where a key comes from...
+ try:
+ stats.update(self.core.get_session_status([key]))
+ except AttributeError:
+ pass
+ stats['num_connections'] = (
+ stats['num_peers'] + stats['peer.num_peers_half_open']
+ )
+ stats['dht_cache_nodes'] = stats['dht.dht_node_cache']
+ stats.update(
+ self.core.get_config_values(
+ ['max_download', 'max_upload', 'max_num_connections']
+ )
+ )
+ # status = self.core.session.status()
+ # for stat in dir(status):
+ # if not stat.startswith('_') and stat not in stats:
+ # stats[stat] = getattr(status, stat, None)
+
+ update_time = time.time()
+ self.last_update[1] = update_time
+
+ # extract the ones we are interested in
+ # adding them to the 1s array
+ for stat, stat_list in self.stats[1].items():
+ if stat in stats:
+ stat_list.insert(0, int(stats[stat]))
+ else:
+ stat_list.insert(0, 0)
+ if len(stat_list) > self.length:
+ stat_list.pop()
+
+ def update_interval(interval, base, multiplier):
+ self.count[interval] = self.count[interval] + 1
+ if self.count[interval] >= interval:
+ self.last_update[interval] = update_time
+ self.count[interval] = 0
+ current_stats = self.stats[interval]
+ for stat, stat_list in self.stats[base].items():
+ try:
+ avg = mean(stat_list[0:multiplier])
+ except ValueError:
+ avg = 0
+ current_stats[stat].insert(0, avg)
+ if len(current_stats[stat]) > self.length:
+ current_stats[stat].pop()
+
+ update_interval(5, 1, 5)
+ update_interval(30, 5, 6)
+ update_interval(300, 30, 10)
+
+ def save_stats(self):
+ self.saved_stats['stats'] = self.stats
+ self.saved_stats.config.update(self.get_totals())
+ self.saved_stats.save()
+
+ # export:
+ @export
+ def get_stats(self, keys, interval):
+ if interval not in self.intervals:
+ return None
+
+ stats_dict = {}
+ for key in keys:
+ if key in self.stats[interval]:
+ stats_dict[key] = self.stats[interval][key]
+
+ stats_dict['_last_update'] = self.last_update[interval]
+ stats_dict['_length'] = self.config['length']
+ stats_dict['_update_interval'] = interval
+ return stats_dict
+
+ @export
+ def get_totals(self):
+ result = {}
+ session_totals = self.get_session_totals()
+ for key in session_totals:
+ result[key] = self.totals[key] + session_totals[key]
+ return result
+
+ @export
+ def get_session_totals(self):
+ return self.core.get_session_status(
+ [
+ 'total_upload',
+ 'total_download',
+ 'total_payload_upload',
+ 'total_payload_download',
+ ]
+ )
+
+ @export
+ def set_config(self, config):
+ """Sets the config dictionary."""
+ for key in config:
+ self.config[key] = config[key]
+ self.config.save()
+
+ @export
+ def get_config(self):
+ """Returns the config dictionary."""
+ return self.config.config
+
+ @export
+ def get_intervals(self):
+ """Returns the available resolutions."""
+ return self.intervals