diff options
-rwxr-xr-x | pttbbs/daemon/angelbeats/angel_perf.py | 113 | ||||
-rw-r--r-- | pttbbs/daemon/angelbeats/angelbeats.c | 17 |
2 files changed, 122 insertions, 8 deletions
diff --git a/pttbbs/daemon/angelbeats/angel_perf.py b/pttbbs/daemon/angelbeats/angel_perf.py new file mode 100755 index 00000000..aea94367 --- /dev/null +++ b/pttbbs/daemon/angelbeats/angel_perf.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +#-*- coding: big5 -*- + +import collections +import math +import os +import time + +BBSHOME = '/home/bbs' +INPUT_FILE = '%s/log/angel_perf.txt' % (BBSHOME) +# A regular week = 590 samples. +SAMPLE_MINIMAL = 200 + +Entry = collections.namedtuple('Entry', 'sample pause1 pause2 max avg std') +DEBUG = None + +PREFIX_DOC = ''' +== 本週小天使活動資料統計結果 (系統自動產生: %s) == + +說明: 天使公會在新小主人找天使時即時統計所有的小天使狀態, + 得到 (1) 小天使當時是否在線上 (2) 神諭呼叫器是否停收/關閉; + 基於上面結果可分析出下列名單。 + + 小天使名稱後面的數字為該小天使被統計到的次數 (以下稱 SAMPLE 數), + 因為 SAMPLE 數字是有人呼叫時才會更新,且為避免惡意洗數字造成結果不公, + 統計每 600 秒最多更新一次(否則有人會趁自己上線開分身狂換小天使), + 所以此數字大略上接近(但不等於)小天使實際上線時間比例。 + + 換句話說,即使為零也不代表此小天使都沒上線過,可能只是上線停留時間都 + 過短或是上線時都沒有新使用者要呼叫小天使。 + +''' % (time.ctime()) + +def is_lazy(e): + # 'LAZY' + '\033[1;33m以下是這段時間內SAMPLE數過低的小天使:\033[m' + # return e.sample < (e.avg - 1.0 * e.std) + return e.sample < 5 + +def is_all_reject2(e): + # 'ALL_REJECT2' + '\033[1;31m以下是關閉呼叫器時間比例過高(與SAMPLE相差小於2)的小天使:\033[m' + return (e.pause2 >= e.sample - 1) + +def parse_perf_file(filename): + data = {} + max_sample = 0 + sum_sample = 0 + sum_sample_square = 0 + with open(filename, 'r') as f: + for l in f: + ls = l.strip() + if ls.startswith('#') or ls == '': + continue + # format: no. uid sample pause1 pause + no, uid, sample, pause1, pause2 = ls.split() + data[uid] = map(int, (sample, pause1, pause2)) + sample = int(sample) + sum_sample += sample + sum_sample_square += sample * sample + if sample > max_sample: + max_sample = sample + N = len(data) or 1 + avg_sample = sum_sample / N + std_sample = math.sqrt((sum_sample_square - + (N * avg_sample * avg_sample)) / N) + return max_sample, avg_sample, std_sample, data + +def get_nick(uid): + fn = '%s/home/%c/%s/angelmsg' % (BBSHOME, uid[0], uid) + nick = '' + if os.path.exists(fn): + nick = open(fn).readline().strip().decode('big5').strip('%%[') + else: + nick = uid + return (nick + '小天使'.decode('big5')).encode('big5') + +def build_badges(max_sample, avg_sample, std_sample, data): + result = {} + filters = [is_all_reject2] + for uid, e in data.items(): + nick = '%s (%d)' % (get_nick(uid), e[0]) + if DEBUG: + nick += ' {%s/%d/%d/%d}' % (uid, e[0], e[1], e[2]) + entry = Entry(e[0], e[1], e[2], max_sample, avg_sample, std_sample) + if is_lazy(entry): + badges = [is_lazy.__doc__] + else: + badges = [f.__doc__ for f in filters if f(entry)] + for b in badges: + if b not in result: + result[b] = [] + result[b] += [nick] + return result + +def main(): + max_sample, avg_sample, std_sample, data = parse_perf_file(INPUT_FILE) + # print 'max=%f, avg=%f, std=%f' % (max_sample, avg_sample, std_sample) + if max_sample < SAMPLE_MINIMAL: + exit() + + print PREFIX_DOC + if DEBUG: + print 'max, avg, std: %d, %d, %d' % (max_sample, avg_sample, std_sample) + else: + print ' SAMPLE 數最大值 / 平均 / 標準差: %d / %d / %d\n' % ( + max_sample, avg_sample, std_sample) + result = build_badges(max_sample, avg_sample, std_sample, data) + for k, v in result.items(): + print '%s:\n %s\n' % (k, '\n '.join(v)) + +if __name__ == '__main__': + main() diff --git a/pttbbs/daemon/angelbeats/angelbeats.c b/pttbbs/daemon/angelbeats/angelbeats.c index fabfb412..b4307d5e 100644 --- a/pttbbs/daemon/angelbeats/angelbeats.c +++ b/pttbbs/daemon/angelbeats/angelbeats.c @@ -361,15 +361,16 @@ create_angel_report(int myuid, angel_beats_report *prpt) { // Print state information. if (from_cmd) { fprintf(stderr, " - %03zu. %-14s: ", i+1, kanade->userid); - if (is_pause) - fprintf(stderr, "[set PAUSE (%d)] ", is_pause); fprintf(stderr, "{samples=%d, pause1=%d, pause2=%d} " - "(masters=%d, activity=%d, assigned=%d, logins=%d)\n", + "(masters=%d, logins=%d, activity=%d, assigned=%d)", kanade->perf.samples, kanade->perf.pause1, - kanade->perf.pause2, - kanade->masters, (int)kanade->last_activity, - (int)kanade->last_assigned, logins); + kanade->perf.pause2, kanade->masters, logins, + (int)kanade->last_activity, + (int)kanade->last_assigned); + if (is_pause) + fprintf(stderr, " [PAUSE %d]", is_pause); + fputc('\n', stderr); } // update report numbers @@ -441,10 +442,10 @@ void export_perf_data(FILE *fp) { time4_t clk = time4(0); AngelInfo *kanade = g_angel_list; fprintf(fp, "# Angel Performance Data (%s)\n", Cdatelite(&clk)); - fprintf(fp, "# No. %-*s Samples Pause1 Pause2\n# ", IDLEN, "UserID"); + fprintf(fp, "# No. %-*s Sample Pause1 Pause2\n# ", IDLEN, "UserID"); print_dash(fp, 70, "# "); for (i = 0; i < g_angel_list_size; i++, kanade++) { - fprintf(fp, "%4lu. %-*s %7d %7d %7d\n", i + 1, IDLEN, kanade->userid, + fprintf(fp, "%4lu. %-*s %6d %6d %6d\n", i + 1, IDLEN, kanade->userid, kanade->perf.samples, kanade->perf.pause1, kanade->perf.pause2); // reset perf data memset(&kanade->perf, 0, sizeof(kanade->perf)); |