bd422d90713c5f8df6847db606f70be0fd418e5d
[quix0rs-apt-p2p.git] / apt_p2p / stats.py
1
2 """Store statistics for the Apt-P2P downloader."""
3
4 from datetime import datetime, timedelta
5 from StringIO import StringIO
6
7 from util import uncompact, byte_format
8
9 class StatsLogger:
10     """Store the statistics for the Khashmir DHT.
11     
12     @ivar startTime: the time the program was started
13     @ivar reachable: whether we can be contacted by other nodes
14     @type table: L{ktable.KTable}
15     @ivar table: the routing table for the DHT
16     @ivar lastTableUpdate: the last time an update of the table stats was done
17     @ivar nodes: the number of nodes connected
18     @ivar users: the estimated number of total users in the DHT
19     @type store: L{db.DB}
20     @ivar store: the database for the DHT
21     @ivar lastDBUpdate: the last time an update of the database stats was done
22     @ivar keys: the number of distinct keys in the database
23     @ivar values: the number of values in the database
24     @ivar downPackets: the number of packets received
25     @ivar upPackets: the number of packets sent
26     @ivar downBytes: the number of bytes received
27     @ivar upBytes: the number of bytes sent
28     @ivar actions: a dictionary of the actions and their statistics, keys are
29         the action name, values are a list of 5 elements for the number of
30         times the action was sent, responded to, failed, received, and
31         generated an error
32     """
33     
34     def __init__(self, db):
35         """Initialize the statistics.
36         
37         @type store: L{db.DB}
38         @param store: the database for the Apt-P2P downloader
39         """
40         # Database
41         self.db = db
42         self.hashes, self.files = 0, 0
43         
44         # Transport
45         self.mirrorDown = 0L
46         self.peerDown = 0L
47         self.peerUp = 0L
48         
49         # Transport All-Time
50         stats = self.db.getStats()
51         self.mirrorAllDown = long(stats.get('mirror_down', 0L))
52         self.peerAllDown = long(stats.get('peer_down', 0L))
53         self.peerAllUp = long(stats.get('peer_up', 0L))
54         
55     def save(self):
56         """Save the persistent statistics to the DB."""
57         stats = {'mirror_down': self.mirrorAllDown,
58                  'peer_down': self.peerAllDown,
59                  'peer_up': self.peerAllUp,
60                  }
61         self.db.saveStats(stats)
62     
63     def formatHTML(self, contactAddress):
64         """Gather statistics for the DHT and format them for display in a browser.
65         
66         @param contactAddress: the external IP address in use
67         @rtype: C{string}
68         @return: the stats, formatted for display in the body of an HTML page
69         """
70         self.hashes, self.files = self.db.dbStats()
71
72         out = StringIO()
73         out.write('<h2>Downloader Statistics</h2>\n')
74         out.write("<table border='0' cellspacing='20px'>\n<tr>\n")
75         out.write('<td>\n')
76
77         # General
78         out.write("<table border='1' cellpadding='4px'>\n")
79         out.write("<tr><th><h3>General</h3></th><th>Value</th></tr>\n")
80         out.write("<tr title='Contact address for this peer'><td>Contact</td><td>" + str(contactAddress) + '</td></tr>\n')
81         out.write("</table>\n")
82         out.write('</td><td>\n')
83         
84         # Database
85         out.write("<table border='1' cellpadding='4px'>\n")
86         out.write("<tr><th><h3>Database</h3></th><th>Value</th></tr>\n")
87         out.write("<tr title='Number of distinct files in the database'><td>Distinct Files</td><td>" + str(self.hashes) + '</td></tr>\n')
88         out.write("<tr title='Total number of files being shared'><td>Total Files</td><td>" + str(self.files) + '</td></tr>\n')
89         out.write("</table>\n")
90         out.write("</td></tr><tr><td colspan='3'>\n")
91         
92         # Transport
93         out.write("<table border='1' cellpadding='4px'>\n")
94         out.write("<tr><th><h3>Transport</h3></th><th>Mirror Downloads</th><th>Peer Downloads</th><th>Peer Uploads</th></tr>\n")
95         out.write("<tr><td title='Since the program was last restarted'>This Session</td>")
96         out.write("<td title='Amount downloaded from mirrors'>" + byte_format(self.mirrorDown) + '</td>')
97         out.write("<td title='Amount downloaded from peers'>" + byte_format(self.peerDown) + '</td>')
98         out.write("<td title='Amount uploaded to peers'>" + byte_format(self.peerUp) + '</td></tr>\n')
99         out.write("<tr><td title='Since the program was last restarted'>Session Ratio</td>")
100         out.write("<td title='Percent of download from mirrors'>%0.2f%%</td>" %
101                   (100.0 * float(self.mirrorDown) / float(max(self.mirrorDown + self.peerDown, 1)), ))
102         out.write("<td title='Percent of download from peers'>%0.2f%%</td>" %
103                   (100.0 * float(self.peerDown) / float(max(self.mirrorDown + self.peerDown, 1)), ))
104         out.write("<td title='Percent uploaded to peers compared with all downloaded'>%0.2f%%</td></tr>\n" %
105                   (100.0 * float(self.peerUp) / float(max(self.mirrorDown + self.peerDown, 1)), ))
106         out.write("<tr><td title='Since the program was installed'>All-Time</td>")
107         out.write("<td title='Amount downloaded from mirrors'>" + byte_format(self.mirrorAllDown) + '</td>')
108         out.write("<td title='Amount downloaded from peers'>" + byte_format(self.peerAllDown) + '</td>')
109         out.write("<td title='Amount uploaded to peers'>" + byte_format(self.peerAllUp) + '</td></tr>\n')
110         out.write("<tr><td title='Since the program was installed'>All-Time Ratio</td>")
111         out.write("<td title='Percent of download from mirrors'>%0.2f%%</td>" %
112                   (100.0 * float(self.mirrorAllDown) / float(max(self.mirrorAllDown + self.peerAllDown, 1)), ))
113         out.write("<td title='Percent of download from peers'>%0.2f%%</td>" %
114                   (100.0 * float(self.peerAllDown) / float(max(self.mirrorAllDown + self.peerAllDown, 1)), ))
115         out.write("<td title='Percent uploaded to peers compared with all downloaded'>%0.2f%%</td></tr\n>" %
116                   (100.0 * float(self.peerAllUp) / float(max(self.mirrorAllDown + self.peerAllDown, 1)), ))
117         out.write("</table>\n")
118         out.write("</td></tr>\n")
119         out.write("</table>\n")
120         
121         return out.getvalue()
122
123     #{ Transport
124     def sentBytes(self, bytes):
125         """Record that some bytes were sent.
126         
127         @param bytes: the number of bytes sent
128         """
129         self.peerUp += bytes
130         self.peerAllUp += bytes
131         
132     def receivedBytes(self, bytes, mirror = False):
133         """Record that some bytes were received.
134         
135         @param bytes: the number of bytes received
136         @param mirror: whether the bytes were sent to a mirror
137         """
138         if mirror:
139             self.mirrorDown += bytes
140             self.mirrorAllDown += bytes
141         else:
142             self.peerDown += bytes
143             self.peerAllDown += bytes