diff --git a/deluge/plugins/graph/create_dev_link.sh b/deluge/plugins/graph/create_dev_link.sh new file mode 100755 index 000000000..0001a4273 --- /dev/null +++ b/deluge/plugins/graph/create_dev_link.sh @@ -0,0 +1,6 @@ +#!/bin/bash +mkdir temp +export PYTHONPATH=./temp +python setup.py build develop --install-dir ./temp +cp ./temp/Graph.egg-link ~/.config/deluge/plugins +rm -fr ./temp diff --git a/deluge/plugins/graph/graph/__init__.py b/deluge/plugins/graph/graph/__init__.py new file mode 100644 index 000000000..fdbca7681 --- /dev/null +++ b/deluge/plugins/graph/graph/__init__.py @@ -0,0 +1,65 @@ +# +# __init__.py +# +# Copyright (C) 2008 Martijn Voncken +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception + +from deluge.log import LOG as log + +from deluge.plugins.init import PluginBase + +class CorePlugin(PluginBase): + def __init__(self, plugin_api, plugin_name): + # Load the Core portion of the plugin + try: + from core import Core + self.plugin = Core(plugin_api, plugin_name) + except Exception, e: + log.debug("Did not load a Core plugin: %s", e) + +class WebUIPlugin(PluginBase): + def __init__(self, plugin_api, plugin_name): + try: + from webui import WebUI + self.plugin = WebUI(plugin_api, plugin_name) + except Exception, e: + log.debug("Did not load a WebUI plugin: %s", e) + +class GtkUIPlugin(PluginBase): + def __init__(self, plugin_api, plugin_name): + # Load the GtkUI portion of the plugin + try: + from gtkui import GtkUI + self.plugin = GtkUI(plugin_api, plugin_name) + except Exception, e: + log.debug("Did not load a GtkUI plugin: %s", e) diff --git a/deluge/plugins/graph/graph/core.py b/deluge/plugins/graph/graph/core.py new file mode 100644 index 000000000..397d68725 --- /dev/null +++ b/deluge/plugins/graph/graph/core.py @@ -0,0 +1,93 @@ +# +# core.py +# +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) Marcos Pinto 2007 +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception + +import deluge +from deluge.log import LOG as log +from deluge.plugins.corepluginbase import CorePluginBase +from deluge import component +import gobject +#from deluge.plugins.coreclient import client #1.1 and later only +#client: see http://dev.deluge-torrent.org/wiki/Development/UiClient#Remoteapi + +DEFAULT_PREFS = { + "test":"NiNiNi", + "update_interval":2000, #2 seconds. + "length":150, # 2 seconds * 150 --> 5 minutes. +} + +""" +port of the old NetworkGraph Plugin. +""" +class Core(CorePluginBase): + + def enable(self): + self.core = component.get("Core") + self.savedUpSpeeds = [] + self.savedDownSpeeds = [] + self.savedConnections = [] + self.config = deluge.configmanager.ConfigManager("graph.conf", DEFAULT_PREFS) + self.update_timer = gobject.timeout_add(self.config.get("update_interval"), self.update_stats) + self.length = self.config.get("length") + + def disable(self): + gobject.source_remove(self.update_timer) + + def update_stats(self): + try: + status = self.core.session.status() + self.savedUpSpeeds.insert(0, int(status.payload_upload_rate)) + if len(self.savedUpSpeeds) > self.length: + self.savedUpSpeeds.pop() + self.savedDownSpeeds.insert(0, int(status.payload_download_rate)) + if len(self.savedDownSpeeds) > self.length: + self.savedDownSpeeds.pop() + except Exception,e: + log.error(e.message) + + return True + + def export_get_upload(self,length=None): + return self.savedUpSpeeds + + def export_get_download(self,length=None): + return self.savedDownSpeeds + + def export_set_config(self, config): + "sets the config dictionary" + for key in config.keys(): + self.config[key] = config[key] + self.config.save() + + def export_get_config(self): + "returns the config dictionary" + return self.config.get_config() diff --git a/deluge/plugins/graph/graph/data/config.glade b/deluge/plugins/graph/graph/data/config.glade new file mode 100644 index 000000000..e39b5204b --- /dev/null +++ b/deluge/plugins/graph/graph/data/config.glade @@ -0,0 +1,27 @@ + + + + + + + + True + + + True + Test config value: + + + + + True + True + + + 1 + + + + + + diff --git a/deluge/plugins/graph/graph/graph.py b/deluge/plugins/graph/graph/graph.py new file mode 100644 index 000000000..4e7be8f0b --- /dev/null +++ b/deluge/plugins/graph/graph/graph.py @@ -0,0 +1,183 @@ +#graph.py +""" +port of old plugin by markybob. +""" +import cairo +import math +from deluge.log import LOG as log +import deluge.common +import time +from deluge.ui.client import aclient + +class NetworkGraph: + def __init__(self): + + self.width = 100 + self.height = 100 + + self.length = 150 + + self.savedUpSpeeds = [] + self.savedDownSpeeds = [] + + self.download_line = True + self.download_fill = True + self.upload_line = True + self.upload_fill = True + self.download_line_color = (0, 0.75,0, 1.0) + self.download_fill_color = (0.6 ,1.1 , 0.6, 1.0) + self.upload_line_color = (0, 0, 1.0, 0.75) + self.upload_fill_color = (0.43,0.43,1.1, 0.5) + self.mean_selected = True + self.legend_selected = True + self.max_selected = True + self.line_size = 4 + self.black = (0, 0 , 0,) + self.interval = 2000 #2secs + self.text_bg = (255, 255 , 255, 128) #prototyping. + + def async_request(self): + """ + convenience method, see test.py + """ + aclient.graph_get_upload(self.set_upload) + aclient.graph_get_download(self.set_download) + aclient.graph_get_config(self.set_config) + + #async callbacks: + def set_config(self, config): + self.length = config["stats_length"] + self.interval = config["update_interval"] + + def set_upload(self , upload): + self.savedUpSpeeds = upload + + def set_download(self , download): + self.savedDownSpeeds = download + + + def draw(self, width, height): + self.width = width + self.height = height + + self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width,self.height) + self.ctx = cairo.Context(self.surface) + + if (self.download_fill or self.download_line) and (self.upload_fill or self.upload_line): + maxSpeed = max(max(self.savedDownSpeeds),max(self.savedUpSpeeds)) + meanSpeed = max(sum(self.savedUpSpeeds) /len(self.savedUpSpeeds), sum(self.savedDownSpeeds)/len(self.savedDownSpeeds)) + elif self.download_fill or self.download_line: + maxSpeed = max(self.savedDownSpeeds) + meanSpeed = sum(self.savedDownSpeeds)/len(self.savedDownSpeeds) + elif self.upload_fill or self.upload_line: + maxSpeed = max(self.savedUpSpeeds) + meanSpeed = sum(self.savedUpSpeeds) /len(self.savedUpSpeeds) + else: + maxSpeed = 0 + + + if maxSpeed > 0: + + if self.max_selected: + self.drawText(deluge.common.fspeed(maxSpeed),4,2) + + if self.download_fill: + self.drawSpeedPoly(self.savedDownSpeeds,self.download_fill_color, maxSpeed, True) + + if self.download_line: + self.drawSpeedPoly(self.savedDownSpeeds,self.download_line_color,maxSpeed, False) + + if self.upload_fill: + self.drawSpeedPoly(self.savedUpSpeeds,self.upload_fill_color,maxSpeed, True) + + if self.upload_line: + self.drawSpeedPoly(self.savedUpSpeeds,self.upload_line_color,maxSpeed, False) + + + if self.mean_selected: + mean = int(self.height - 1 - ((self.height-28)*meanSpeed/maxSpeed)) + + self.drawLine(self.black, 0,mean, self.width, mean) + self.drawText(deluge.common.fspeed(meanSpeed), 4, mean - 12 - 2) + + if self.legend_selected: + self.drawLegend() + return self.surface + + + def tracePath(self, speeds, maxSpeed): + lineWidth = self.line_size + + self.ctx.set_line_width(lineWidth) + self.ctx.move_to(self.width + lineWidth,self.height + lineWidth) + self.ctx.line_to(self.width + lineWidth,int(self.height-((self.height-28)*speeds[0]/maxSpeed))) + + for i in range(len(speeds)): + self.ctx.line_to(int(self.width-1-((i*self.width)/(self.length-1))),int(self.height-1-((self.height-28)*speeds[i]/maxSpeed))) + + self.ctx.line_to(int(self.width-1-(((len(speeds)-1)*self.width)/(self.length-1))),int(self.height)-1 + lineWidth) + self.ctx.close_path() + + def drawSpeedPoly(self, speeds, color, maxSpeed, fill): + self.tracePath(speeds, maxSpeed) + self.ctx.set_source_rgba(color[0],color[1],color[2], color[3]) + + if fill: + self.ctx.fill() + else: + self.ctx.stroke() + + def drawLegend(self): + showDown = self.download_fill or self.download_line + showUp = self.upload_fill or self.upload_line + downBox_X = self.width-113 + + self.drawText("Download:", self.width-180,3) + self.drawText("Upload:", self.width-80,3) + + if self.download_fill and self.download_line: + self.drawRect(self.download_line_color,downBox_X,5,12,12) + self.drawRect(self.download_fill_color,downBox_X+12,5,12,12) + elif self.download_fill: + self.drawRect(self.download_fill_color,downBox_X,5,24,12) + elif self.download_line: + self.drawRect(self.download_line_color,downBox_X,5,24,12) + + if self.upload_fill and self.upload_line: + self.drawRect(self.upload_line_color,self.width-30,5,12,12) + self.drawRect(self.upload_fill_color,self.width-18,5,12,12) + elif self.upload_fill: + self.drawRect(self.upload_fill_color,self.width-30,5,24,12) + elif self.upload_line: + self.drawRect(self.upload_line_color,self.width-30,5,24,12) + + if True: + txt_end = time.strftime("%H:%M:%S") + txt_start = time.strftime("%H:%M:%S",time.localtime(time.time() - self.length * (self.interval / 1000.0) )) + self.length * self.interval + self.drawText(txt_end, self.width-60,self.height - 20) + self.drawText(txt_start, 4 ,self.height - 20) + + + def drawText(self,text,x,y): + self.ctx.set_font_size(12) + self.ctx.move_to(x, y +12) + self.ctx.set_source_rgba(*self.black) + self.ctx.show_text(text) + + def drawRect(self,color,x,y,height,width): + self.ctx.set_source_rgba(color[0],color[1],color[2],color[3],) + self.ctx.rectangle(x,y,height,width) + self.ctx.fill() + + def drawLine(self,color,x1,y1,x2,y2): + self.ctx.set_source_rgba(*color) + self.ctx.set_line_width(1) + self.ctx.move_to(x1, y1) + self.ctx.line_to(x2, y2) + self.ctx.stroke() + +if __name__ == "__main__": + import test + + diff --git a/deluge/plugins/graph/graph/gtkui.py b/deluge/plugins/graph/graph/gtkui.py new file mode 100644 index 000000000..933a2ade4 --- /dev/null +++ b/deluge/plugins/graph/graph/gtkui.py @@ -0,0 +1,75 @@ +# +# gtkui.py +# +# Copyright (C) 2008 Martijn Voncken +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception + +from deluge.log import LOG as log +from deluge.ui.client import aclient +import gtk + +class GtkUI(object): + def __init__(self, plugin_api, plugin_name): + log.debug("Calling Graph UI init") + self.plugin = plugin_api + + def enable(self): + self.glade = gtk.glade.XML(self.get_resource("config.glade")) + + self.plugin.add_preferences_page("Graph", self.glade.get_widget("prefs_box")) + self.plugin.register_hook("on_apply_prefs", self.on_apply_prefs) + self.plugin.register_hook("on_show_prefs", self.on_show_prefs) + self.on_show_prefs() + + def disable(self): + self.plugin.remove_preferences_page("Graph") + self.plugin.deregister_hook("on_apply_prefs", self.on_apply_prefs) + self.plugin.deregister_hook("on_show_prefs", self.on_show_prefs) + + def on_apply_prefs(self): + log.debug("applying prefs for Graph") + config = { + "test":self.glade.get_widget("txt_test").get_text() + } + aclient.graph_set_config(None, config) + + def on_show_prefs(self): + aclient.graph_get_config(self.cb_get_config) + + def cb_get_config(self, config): + "callback for on show_prefs" + self.glade.get_widget("txt_test").set_text(config["test"]) + + def get_resource(self, filename): + import pkg_resources, os + return pkg_resources.resource_filename("graph", os.path.join("data", filename)) diff --git a/deluge/plugins/graph/graph/template/graph.html b/deluge/plugins/graph/graph/template/graph.html new file mode 100644 index 000000000..2f4b0fdf7 --- /dev/null +++ b/deluge/plugins/graph/graph/template/graph.html @@ -0,0 +1,10 @@ +$:render.header(_("Network Graph"), 'graph') +$:render.admin_toolbar('graph') + +
+ +
+ + + +$:render.footer() diff --git a/deluge/plugins/graph/graph/test.py b/deluge/plugins/graph/graph/test.py new file mode 100644 index 000000000..8081a3d94 --- /dev/null +++ b/deluge/plugins/graph/graph/test.py @@ -0,0 +1,60 @@ +from deluge.ui.client import sclient, aclient +sclient.set_core_uri() +from graph import NetworkGraph + + +def test_sync(): + if 1: + upload = sclient.graph_get_upload() + download = sclient.graph_get_download() + print upload + print download + else: + upload = [66804, 66915, 66974, 67447, 67540, 67318, 67320, 67249, 66659, 66489, 67027, 66914, 66802, 67303, 67654, 67643, 67763, 67528, 67523, 67431, 67214, 66939, 67316, 67020, 66881, 67103, 67377, 67141, 67366, 67492, 67375, 67203, 67056, 67010, 67029, 66741, 66695, 66868, 66805, 66264, 66249, 66317, 66459, 66306, 66681, 66954, 66662, 66278, 65921, 65695, 65681, 65942, 66000, 66140, 66424, 66480, 66257, 66271, 66145, 65854, 65568, 65268, 65112, 65050, 65027, 64676, 64655, 64178, 64386, 63979, 63271, 62746, 62337, 62297, 62496, 62902, 63801, 64121, 62957, 62921, 63051, 62644, 63240, 64107, 63968, 63987, 63644, 63263, 63153, 62999, 62843, 62777, 63101, 63078, 63178, 63126, 63401, 62630, 62451, 62505, 62254, 61485, 61264, 60937, 60568, 61011, 61109, 60325, 60196, 59640, 59619, 59514, 60813, 60572, 61632, 61689, 63365, 64583, 66396, 67179, 68209, 68295, 67674, 67559, 67195, 66178, 65632, 66124, 66456, 66676, 67183, 67620, 66960, 66347, 65925, 65907, 65896, 66738, 66703, 67060, 67004, 67007, 66329, 65304, 52002, 38969, 25433, 12426, 0, 0] + download = [42926, 43853, 43157, 45470, 44254, 46272, 45083, 47344, 46716, 51963, 50112, 52334, 55525, 57545, 53691, 51637, 49574, 49836, 48295, 49843, 52878, 56014, 56966, 56938, 60065, 60461, 56542, 59526, 58678, 54424, 51862, 55109, 52132, 53783, 51687, 56567, 52182, 50758, 46714, 50511, 48161, 50920, 48694, 50528, 55074, 55420, 55882, 59268, 59958, 57938, 57115, 51424, 51180, 53184, 52879, 51177, 54417, 51097, 47901, 49870, 55865, 61118, 61476, 63498, 58878, 49630, 45975, 45632, 45892, 44855, 49495, 48304, 45829, 42152, 39403, 37574, 32384, 34933, 34901, 33492, 31953, 36271, 33826, 34515, 36408, 41106, 43054, 44110, 40810, 41383, 37267, 35881, 38660, 37525, 34857, 36718, 36842, 34281, 39528, 41854, 42952, 40021, 41722, 41045, 42917, 39287, 38672, 32824, 28765, 22686, 18490, 15714, 15268, 14793, 15305, 16354, 16720, 17502, 17857, 16622, 18447, 19929, 31138, 36965, 36158, 32795, 30445, 21997, 18100, 22491, 27227, 29317, 32436, 35700, 39140, 36258, 33697, 24751, 20354, 8211, 3836, 1560, 834, 2034, 1744, 1637, 1637, 1637, 0, 0] + + from graph import NetworkGraph + n = NetworkGraph() + n.savedUpSpeeds = upload + n.savedDownSpeeds = download + + n.draw(800,200) + n.surface.write_to_png('output_sync.png') + +def test_async(): + n = NetworkGraph() + n.async_request() + aclient.force_call(True) + surface = n.draw(600,300) + surface.write_to_png('output_async.png') + +def test_write(): + """ + writing to a file-like object; need this for webui. + """ + class fake_file: + def __init__(self): + self.data = [] + def write(self, str): + self.data.append(str) + + n = NetworkGraph() + n.async_request() + aclient.force_call(True) + surface = n.draw(900,150) + + file_like = fake_file() + surface.write_to_png(file_like) + data = "".join(file_like.data) + + f = open("file_like.png","wb") + f.write(data) + f.close() + + +test_sync() +test_async() +test_write() + + + diff --git a/deluge/plugins/graph/graph/webui.py b/deluge/plugins/graph/graph/webui.py new file mode 100644 index 000000000..2d4a5a994 --- /dev/null +++ b/deluge/plugins/graph/graph/webui.py @@ -0,0 +1,101 @@ +# +# webui.py +# +# Copyright (C) 2008 Martijn Voncken +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception + +import os +from deluge.log import LOG as log +from deluge.ui.client import sclient, aclient +from deluge.plugins.webuipluginbase import WebUIPluginBase +from deluge import component +from graph import NetworkGraph + +api = component.get("WebPluginApi") +forms = api.forms + +#pages: +class graph_page: + @api.deco.deluge_page + def GET(self, args): + return api.render.graph.graph() + +class network_png: + @api.deco.check_session + def GET(self, args): + vars = api.web.input(width = 600, height = 150) + log.debug("1") + api.web.header("Content-Type", "image/png") + n = NetworkGraph() + n.async_request() + aclient.force_call(True) + self.data = "" + surface = n.draw(int(vars.width), int(vars.height)) + surface.write_to_png(self) + print self.data + + def write(self, str): #file like object for pango; write_to_png + self.data += str + + + +class WebUI(WebUIPluginBase): + #map url's to classes: [(url,class), ..] + urls = [ + ('/graph', graph_page), + ('/graph/network.png', network_png) + ] + + def enable(self): + api.config_page_manager.register('plugins', 'graph' ,ConfigForm) + api.menu_manager.register_admin_page("graph", _("Graph"), "/graph") #<--top menu + + def disable(self): + api.config_page_manager.deregister('graph') + api.menu_manager.deregister_admin_page("graph") #<--top menu + + +class ConfigForm(forms.Form): + #meta: + title = _("Graph") + + #load/save: + def initial_data(self): + return sclient.graph_get_config() + + def save(self, data): + cfg = dict(data) + sclient.graph_set_config(cfg) + + #django newforms magic: define config fields: + test = forms.CharField(label=_("Test config value")) diff --git a/deluge/plugins/graph/setup.py b/deluge/plugins/graph/setup.py new file mode 100644 index 000000000..401c64572 --- /dev/null +++ b/deluge/plugins/graph/setup.py @@ -0,0 +1,56 @@ +# +# setup.py +# +# Copyright (C) 2008 Martijn Voncken +# +# Basic plugin template created by: +# Copyright (C) 2008 Martijn Voncken +# Copyright (C) 2007, 2008 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception + +from setuptools import setup + +__author__ = "Martijn Voncken " + +setup( + name="Graph", + version="0.1", + description=__doc__, + author=__author__, + packages=["graph"], + package_data = {"graph": ["template/*","data/*"]}, + entry_points=""" + [deluge.plugin.core] + Graph = graph:CorePlugin + [deluge.plugin.webui] + Graph = graph:WebUIPlugin + [deluge.plugin.gtkui] + Graph = graph:GtkUIPlugin + """ +)