source: deluge/ui/sessionproxy.py@ dd1716

2.0.x develop extjs4-port
Last change on this file since dd1716 was 84b33c3, checked in by Andrew Resch <andrewresch@gmail.com>, 15 years ago

Fix get_torrent_status by creating a status dict from the cache after it's been updated

  • Property mode set to 100644
File size: 8.1 KB
Line 
1#
2# sessionproxy.py
3#
4# Copyright (C) 2010 Andrew Resch <andrewresch@gmail.com>
5#
6# Deluge is free software.
7#
8# You may redistribute it and/or modify it under the terms of the
9# GNU General Public License, as published by the Free Software
10# Foundation; either version 3 of the License, or (at your option)
11# any later version.
12#
13# deluge is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16# See the GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with deluge. If not, write to:
20# The Free Software Foundation, Inc.,
21# 51 Franklin Street, Fifth Floor
22# Boston, MA 02110-1301, USA.
23#
24# In addition, as a special exception, the copyright holders give
25# permission to link the code of portions of this program with the OpenSSL
26# library.
27# You must obey the GNU General Public License in all respects for all of
28# the code used other than OpenSSL. If you modify file(s) with this
29# exception, you may extend this exception to your version of the file(s),
30# but you are not obligated to do so. If you do not wish to do so, delete
31# this exception statement from your version. If you delete this exception
32# statement from all source files in the program, then also delete it here.
33#
34#
35
36from twisted.internet.defer import maybeDeferred, succeed
37
38import deluge.component as component
39from deluge.ui.client import client
40from deluge.log import LOG as log
41import time
42
43class SessionProxy(component.Component):
44 """
45 The SessionProxy component is used to cache session information client-side
46 to reduce the number of RPCs needed to provide a rich user interface.
47
48 On start-up it will query the Core for a full status of all the torrents in
49 the session. After that point, it will query the Core for only changes in
50 the status of the torrents and will try to satisfy client requests from the
51 cache.
52
53 """
54 def __init__(self):
55 log.debug("SessionProxy init..")
56 component.Component.__init__(self, "SessionProxy", interval=5)
57
58 # Set the cache time in seconds
59 # This is how long data will be valid before refetching from the core
60 self.cache_time = 1.5
61
62 # Hold the torrents' status.. {torrent_id: [time, {status_dict}], ...}
63 self.torrents = {}
64
65 client.register_event_handler("TorrentStateChangedEvent", self.on_torrent_state_changed)
66 client.register_event_handler("TorrentRemovedEvent", self.on_torrent_removed)
67 client.register_event_handler("TorrentAddedEvent", self.on_torrent_added)
68
69 def start(self):
70 def on_torrent_status(status):
71 # Save the time we got the torrent status
72 t = time.time()
73 for key, value in status.items():
74 self.torrents[key] = [t, value]
75
76 return client.core.get_torrents_status({}, [], True).addCallback(on_torrent_status)
77
78 def stop(self):
79 self.torrents = {}
80
81 def create_status_dict(self, torrent_ids, keys):
82 """
83 Creates a status dict from the cache.
84
85 :param torrent_ids: the torrent_ids
86 :type torrent_ids: list of strings
87 :param keys: the status keys
88 :type keys: list of strings
89
90 :returns: a dict with the status information for the *torrent_ids*
91 :rtype: dict
92
93 """
94 sd = {}
95 for torrent_id in torrent_ids:
96 sd[torrent_id] = dict([(x, y) for x, y in self.torrents[torrent_id][1].iteritems() if x in keys])
97 return sd
98
99 def get_torrent_status(self, torrent_id, keys):
100 """
101 Get a status dict for one torrent.
102
103 :param torrent_id: the torrent_id
104 :type torrent_id: string
105 :param keys: the status keys
106 :type keys: list of strings
107
108 :returns: a dict of status information
109 :rtype: dict
110
111 """
112 if torrent_id in self.torrents:
113 if time.time() - self.torrents[torrent_id][0] < self.cache_time:
114 return succeed(self.create_status_dict([torrent_id], keys)[torrent_id])
115 else:
116 d = client.core.get_torrent_status(torrent_id, keys, True)
117 def on_status(result, torrent_id):
118 self.torrents[torrent_id][0] = time.time()
119 self.torrents[torrent_id][1].update(result)
120 return self.create_status_dict([torrent_id], keys)[torrent_id]
121 return d.addCallback(on_status, torrent_id)
122 else:
123 d = client.core.get_torrent_status(torrent_id, keys, True)
124 def on_status(result):
125 if result:
126 self.torrents[torrent_id] = (time.time(), result)
127 return result
128 return d.addCallback(on_status)
129
130 def get_torrents_status(self, filter_dict, keys):
131 """
132 Get a dict of torrent statuses.
133
134 The filter can take 2 keys, *state* and *id*. The state filter can be
135 one of the torrent states or the special one *Active*. The *id* key is
136 simply a list of torrent_ids.
137
138 :param filter_dict: the filter used for this query
139 :type filter_dict: dict
140 :param keys: the status keys
141 :type keys: list of strings
142
143 :returns: a dict of torrent_ids and their status dicts
144 :rtype: dict
145
146 """
147 # Helper functions and callbacks ---------------------------------------
148 def on_status(result, torrent_ids, keys):
149 # Update the internal torrent status dict with the update values
150 t = time.time()
151 for key, value in result.items():
152 self.torrents[key][0] = t
153 self.torrents[key][1].update(value)
154
155 # Create the status dict
156 if not torrent_ids:
157 torrent_ids = result.keys()
158
159 return self.create_status_dict(torrent_ids, keys)
160
161 def find_torrents_to_fetch(torrent_ids):
162 to_fetch = []
163 t = time.time()
164 for key in torrent_ids:
165 torrent = self.torrents[key]
166 if t - torrent[0] > self.cache_time:
167 to_fetch.append(key)
168
169 return to_fetch
170 #-----------------------------------------------------------------------
171
172 if not filter_dict:
173 # This means we want all the torrents status
174 # We get a list of any torrent_ids with expired status dicts
175 to_fetch = find_torrents_to_fetch(self.torrents.keys())
176 if to_fetch:
177 d = client.core.get_torrents_status({"id": to_fetch}, keys, True)
178 return d.addCallback(on_status, self.torrents.keys(), keys)
179
180 # Don't need to fetch anything
181 return maybeDeferred(self.create_status_dict, self.torrents.keys(), keys)
182
183
184 if len(filter_dict) == 1 and "id" in filter_dict:
185 # At this point we should have a filter with just "id" in it
186 to_fetch = find_torrents_to_fetch(filter_dict["id"])
187 if to_fetch:
188 d = client.core.get_torrents_status({"id": to_fetch}, keys, True)
189 return d.addCallback(on_status, filter_dict["id"], keys)
190 else:
191 # Don't need to fetch anything, so just return data from the cache
192 return maybeDeferred(self.create_status_dict, filter_dict["id"], keys)
193 else:
194 # This is a keyworded filter so lets just pass it onto the core
195 # XXX: Add more caching here.
196 d = client.core.get_torrents_status(filter_dict, keys, True)
197 return d.addCallback(on_status, None, keys)
198
199 def on_torrent_state_changed(self, torrent_id, state):
200 self.torrents[torrent_id][1]["state"] = state
201
202 def on_torrent_added(self, torrent_id):
203 self.torrents[torrent_id] = [time.time() - self.cache_time - 1, {}]
204 def on_status(status):
205 self.torrents[torrent_id][1].update(status)
206 client.core.get_torrent_status(torrent_id, []).addCallback(on_status)
207
208 def on_torrent_removed(self, torrent_id):
209 del self.torrents[torrent_id]
Note: See TracBrowser for help on using the repository browser.