Merge branch 'usage'
This commit is contained in:
commit
7426097ba5
|
@ -18,3 +18,18 @@ CREATE TABLE `messages`
|
||||||
`when` INTEGER
|
`when` INTEGER
|
||||||
);
|
);
|
||||||
CREATE INDEX `messages_idx` ON `messages` (`appid`, `channelid`);
|
CREATE INDEX `messages_idx` ON `messages` (`appid`, `channelid`);
|
||||||
|
|
||||||
|
CREATE TABLE `usage`
|
||||||
|
(
|
||||||
|
`started` INTEGER, -- seconds since epoch, rounded to one day
|
||||||
|
`result` VARCHAR, -- happy, scary, lonely, errory, pruney
|
||||||
|
-- "happy": both sides close with mood=happy
|
||||||
|
-- "scary": any side closes with mood=scary (bad MAC, probably wrong pw)
|
||||||
|
-- "lonely": any side closes with mood=lonely (no response from 2nd side)
|
||||||
|
-- "errory": any side closes with mood=errory (other errors)
|
||||||
|
-- "pruney": channels which get pruned for inactivity
|
||||||
|
-- "crowded": three or more sides were involved
|
||||||
|
`total_time` INTEGER, -- seconds from start to closed, or None
|
||||||
|
`waiting_time` INTEGER -- seconds from start to 2nd side appearing, or None
|
||||||
|
);
|
||||||
|
CREATE INDEX `usage_idx` ON `usage` (`started`);
|
||||||
|
|
|
@ -274,9 +274,72 @@ class Channel:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _store_summary(self, summary):
|
||||||
|
(started, result, total_time, waiting_time) = summary
|
||||||
|
self._db.execute("INSERT INTO `usage`"
|
||||||
|
" (`started`, `result`, `total_time`, `waiting_time`)"
|
||||||
|
" VALUES (?,?,?,?)",
|
||||||
|
(started, result, total_time, waiting_time))
|
||||||
|
self._db.commit()
|
||||||
|
|
||||||
|
def _summarize(self, messages, delete_time):
|
||||||
|
all_sides = set([m["side"] for m in messages])
|
||||||
|
if len(all_sides) == 0:
|
||||||
|
log.msg("_summarize was given zero messages") # shouldn't happen
|
||||||
|
return
|
||||||
|
|
||||||
|
started = min([m["when"] for m in messages])
|
||||||
|
# 'total_time' is how long the channel was occupied. That ends now,
|
||||||
|
# both for channels that got pruned for inactivity, and for channels
|
||||||
|
# that got pruned because of two DEALLOCATE messages
|
||||||
|
total_time = delete_time - started
|
||||||
|
|
||||||
|
if len(all_sides) == 1:
|
||||||
|
return (started, "lonely", total_time, None)
|
||||||
|
if len(all_sides) > 2:
|
||||||
|
# TODO: it'll be useful to have more detail here
|
||||||
|
return (started, "crowded", total_time, None)
|
||||||
|
|
||||||
|
# exactly two sides were involved
|
||||||
|
A_side = sorted(messages, key=lambda m: m["when"])[0]["side"]
|
||||||
|
B_side = list(all_sides - set([A_side]))[0]
|
||||||
|
|
||||||
|
# How long did the first side wait until the second side showed up?
|
||||||
|
first_A = min([m["when"] for m in messages if m["side"] == A_side])
|
||||||
|
first_B = min([m["when"] for m in messages if m["side"] == B_side])
|
||||||
|
waiting_time = first_B - first_A
|
||||||
|
|
||||||
|
# now, were all sides closed? If not, this is "pruney"
|
||||||
|
A_deallocs = [m for m in messages
|
||||||
|
if m["phase"] == DEALLOCATE and m["side"] == A_side]
|
||||||
|
B_deallocs = [m for m in messages
|
||||||
|
if m["phase"] == DEALLOCATE and m["side"] == B_side]
|
||||||
|
if not A_deallocs or not B_deallocs:
|
||||||
|
return (started, "pruney", total_time, None)
|
||||||
|
|
||||||
|
# ok, both sides closed. figure out the mood
|
||||||
|
A_mood = A_deallocs[0]["body"] # maybe None
|
||||||
|
B_mood = B_deallocs[0]["body"] # maybe None
|
||||||
|
mood = "quiet"
|
||||||
|
if A_mood == u"happy" and B_mood == u"happy":
|
||||||
|
mood = "happy"
|
||||||
|
if A_mood == u"lonely" or B_mood == u"lonely":
|
||||||
|
mood = "lonely"
|
||||||
|
if A_mood == u"errory" or B_mood == u"errory":
|
||||||
|
mood = "errory"
|
||||||
|
if A_mood == u"scary" or B_mood == u"scary":
|
||||||
|
mood = "scary"
|
||||||
|
return (started, mood, total_time, waiting_time)
|
||||||
|
|
||||||
def delete_and_summarize(self):
|
def delete_and_summarize(self):
|
||||||
# TODO: summarize usage, write into DB
|
|
||||||
db = self._db
|
db = self._db
|
||||||
|
c = self._db.execute("SELECT * FROM `messages`"
|
||||||
|
" WHERE `appid`=? AND `channelid`=?"
|
||||||
|
" ORDER BY `when`",
|
||||||
|
(self._appid, self._channelid))
|
||||||
|
messages = c.fetchall()
|
||||||
|
summary = self._summarize(messages, time.time())
|
||||||
|
self._store_summary(summary)
|
||||||
db.execute("DELETE FROM `messages`"
|
db.execute("DELETE FROM `messages`"
|
||||||
" WHERE `appid`=? AND `channelid`=?",
|
" WHERE `appid`=? AND `channelid`=?",
|
||||||
(self._appid, self._channelid))
|
(self._appid, self._channelid))
|
||||||
|
|
|
@ -8,6 +8,7 @@ from twisted.internet.threads import deferToThread
|
||||||
from twisted.web.client import getPage, Agent, readBody
|
from twisted.web.client import getPage, Agent, readBody
|
||||||
from .. import __version__
|
from .. import __version__
|
||||||
from .common import ServerBase
|
from .common import ServerBase
|
||||||
|
from ..servers import relay_server
|
||||||
from ..twisted.eventsource_twisted import EventSource
|
from ..twisted.eventsource_twisted import EventSource
|
||||||
|
|
||||||
class Reachable(ServerBase, unittest.TestCase):
|
class Reachable(ServerBase, unittest.TestCase):
|
||||||
|
@ -354,3 +355,71 @@ class OneEventAtATime:
|
||||||
def disconnected(self, why):
|
def disconnected(self, why):
|
||||||
self.disconnected_d.callback((why,))
|
self.disconnected_d.callback((why,))
|
||||||
|
|
||||||
|
class Summary(unittest.TestCase):
|
||||||
|
def test_summarize(self):
|
||||||
|
c = relay_server.Channel(None, None, None, None, None)
|
||||||
|
A = relay_server.ALLOCATE
|
||||||
|
D = relay_server.DEALLOCATE
|
||||||
|
|
||||||
|
messages = [{"when": 1, "side": "a", "phase": A}]
|
||||||
|
self.failUnlessEqual(c._summarize(messages, 2),
|
||||||
|
(1, "lonely", 1, None))
|
||||||
|
|
||||||
|
messages = [{"when": 1, "side": "a", "phase": A},
|
||||||
|
{"when": 2, "side": "a", "phase": D, "body": "lonely"},
|
||||||
|
]
|
||||||
|
self.failUnlessEqual(c._summarize(messages, 3),
|
||||||
|
(1, "lonely", 2, None))
|
||||||
|
|
||||||
|
messages = [{"when": 1, "side": "a", "phase": A},
|
||||||
|
{"when": 2, "side": "b", "phase": A},
|
||||||
|
{"when": 3, "side": "c", "phase": A},
|
||||||
|
]
|
||||||
|
self.failUnlessEqual(c._summarize(messages, 4),
|
||||||
|
(1, "crowded", 3, None))
|
||||||
|
|
||||||
|
base = [{"when": 1, "side": "a", "phase": A},
|
||||||
|
{"when": 2, "side": "a", "phase": "pake", "body": "msg1"},
|
||||||
|
{"when": 10, "side": "b", "phase": "pake", "body": "msg2"},
|
||||||
|
{"when": 11, "side": "b", "phase": "data", "body": "msg3"},
|
||||||
|
{"when": 20, "side": "a", "phase": "data", "body": "msg4"},
|
||||||
|
]
|
||||||
|
def make_moods(A_mood, B_mood):
|
||||||
|
return base + [
|
||||||
|
{"when": 21, "side": "a", "phase": D, "body": A_mood},
|
||||||
|
{"when": 30, "side": "b", "phase": D, "body": B_mood},
|
||||||
|
]
|
||||||
|
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("happy", "happy"), 41),
|
||||||
|
(1, "happy", 40, 9))
|
||||||
|
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("scary", "happy"), 41),
|
||||||
|
(1, "scary", 40, 9))
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("happy", "scary"), 41),
|
||||||
|
(1, "scary", 40, 9))
|
||||||
|
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("lonely", "happy"), 41),
|
||||||
|
(1, "lonely", 40, 9))
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("happy", "lonely"), 41),
|
||||||
|
(1, "lonely", 40, 9))
|
||||||
|
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("errory", "happy"), 41),
|
||||||
|
(1, "errory", 40, 9))
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("happy", "errory"), 41),
|
||||||
|
(1, "errory", 40, 9))
|
||||||
|
|
||||||
|
# scary trumps other moods
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("scary", "lonely"), 41),
|
||||||
|
(1, "scary", 40, 9))
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods("scary", "errory"), 41),
|
||||||
|
(1, "scary", 40, 9))
|
||||||
|
|
||||||
|
# older clients don't send a mood
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods(None, None), 41),
|
||||||
|
(1, "quiet", 40, 9))
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods(None, "happy"), 41),
|
||||||
|
(1, "quiet", 40, 9))
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods(None, "happy"), 41),
|
||||||
|
(1, "quiet", 40, 9))
|
||||||
|
self.failUnlessEqual(c._summarize(make_moods(None, "scary"), 41),
|
||||||
|
(1, "scary", 40, 9))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user