#!/usr/bin/python
# -*- coding: utf-8 -*-
# @todo: 服务器留存分析, 仅针对单 serverID 统计,不管合服后
import CommFunc
import ConfigParser
import mylog
import logging
cfg = ConfigParser.ConfigParser()
cfg.read("../../InterfaceConfig.php")
ServerPath = cfg.get("ServerInfo", "ServerPath")
# 需要处理的流向名及顺序列表
DRNameList = ["FirstLogin", "LogInOut", "CTGOK"]
def queryServerKeepLoginInfo(argvDict):
logging.info("queryServerKeepLoginInfo %s" % str(argvDict))
if "serverID" not in argvDict:
print "not serverID"
return
argvDict["OnlyServerID"] = argvDict["serverID"] # 只查本服
loginDayList = eval(argvDict.get("loginDayList", str(range(2, 22)))) # 留存天(当日)
maxDay = max(loginDayList)
logging.info("loginDayList %s" % str(loginDayList))
startDate = argvDict.get("startDate", "")
endDate = argvDict.get("endDate", "")
eventType = argvDict.get("eventType", "")
createRoleEndDate = argvDict.get("createRoleEndDate", "")
if not createRoleEndDate:
createRoleEndDate = endDate if endDate else startDate
argvDict["createRoleEndDate"] = createRoleEndDate # 留存创角/首充结束天
createRoleEndDateTime = CommFunc.strToDatetime(createRoleEndDate)
endDateTime = CommFunc.getDiffDaysDate(maxDay - 1, createRoleEndDateTime)
argvDict["endDate"] = str(endDateTime)[:10]
logging.info(" upd endDate=%s" % endDate)
logging.info(" upd createRoleEndDate=%s" % createRoleEndDate)
else:
createRoleEndDateTime = CommFunc.strToDatetime(createRoleEndDate)
doCount = 0
logInOutDateList = []
createRoleDateTime = CommFunc.strToDatetime(startDate)
logging.info("createRoleDateTime=%s" % createRoleDateTime)
logging.info("createRoleEndDateTime=%s" % createRoleEndDateTime)
while createRoleDateTime <= createRoleEndDateTime and doCount < 365:
doCount += 1
for loginDay in loginDayList:
loginDateTime = CommFunc.getDiffDaysDate(loginDay - 1, createRoleDateTime)
loginDate = str(loginDateTime)[:10]
if loginDate not in logInOutDateList:
logInOutDateList.append(loginDate)
createRoleDateTime = CommFunc.getDiffDaysDate(1, createRoleDateTime)
logInOutDateList.sort()
startDate = argvDict.get("startDate", "")
endDate = argvDict.get("endDate", "")
createDateAccIDInfo = {} # {"首登日期":["账号", ...], ...}
loginDateAccIDInfo = {} # {"登录日期":["账号", ...], ...}
firstCTGRealAccIDInfo = {} # {"首充日期":["账号", ...], ...}
# 查询中心备份的
if CommFunc.isQueryCenterbak(argvDict):
queryCenterBak(startDate, endDate, logInOutDateList, argvDict)
return
needQueryCenterbak = CommFunc.loopMainServerDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc, parseLineFunc,
createDateAccIDInfo, loginDateAccIDInfo, logInOutDateList, firstCTGRealAccIDInfo, argvDict, drNameList=DRNameList)
logging.info("needQueryCenterbak %s" % needQueryCenterbak)
if needQueryCenterbak:
bakDataInfo = CommFunc.queryBackupCenterDR(cfg, argvDict)
logging.info("queryBackupCenterDR OK")
if bakDataInfo == None:
return
createDateAccIDInfoBak, loginDateAccIDInfoBak, firstCTGRealAccIDInfoBak = bakDataInfo
# 合并数据
for bakDate, firstLoginAccIDList in createDateAccIDInfoBak.items():
if bakDate not in createDateAccIDInfo:
createDateAccIDInfo[bakDate] = firstLoginAccIDList
else:
createDateAccIDInfo[bakDate] = list(set(firstLoginAccIDList + createDateAccIDInfo[bakDate]))
for bakDate, firstCTGRealAccIDList in firstCTGRealAccIDInfoBak.items():
if bakDate not in firstCTGRealAccIDInfo:
firstCTGRealAccIDInfo[bakDate] = firstCTGRealAccIDList
else:
firstCTGRealAccIDInfo[bakDate] = list(set(firstCTGRealAccIDList + firstCTGRealAccIDInfo[bakDate]))
for bakDate, loginAccIDList in loginDateAccIDInfoBak.items():
if bakDate not in loginDateAccIDInfo:
loginDateAccIDInfo[bakDate] = loginAccIDList
else:
loginDateAccIDInfo[bakDate] = list(set(loginAccIDList + loginDateAccIDInfo[bakDate]))
logging.info("query all data OK")
loginStyleInfo = {}
loginTDList = ["DateStr", "FirstAccIDCount"]
for loginDay in loginDayList:
loginTDList.append(loginDay)
title = _(u"次日留存") if loginDay == 2 else (_(u"第%s日留存") % loginDay)
loginStyleInfo[loginDay] = {"align":"left", "title":title}
# 创角用户留存
loginDrList = calcLoginDr(createDateAccIDInfo, loginDayList, loginDateAccIDInfo)
# 首充用户留存
ctgLoginDrList = calcLoginDr(firstCTGRealAccIDInfo, loginDayList, loginDateAccIDInfo)
printStr = "
%s
" % _(u"留存分析")
if startDate:
printStr += "%s: %s
" % (_(u"查询统计开始日期"), startDate)
if createRoleEndDate:
printStr += "%s: %s
" % (_(u"查询统计结束日期"), createRoleEndDate)
printStr += "
"
# 表格输出
printStr += "【%s】
" % _(u"创角留存")
loginStyleInfo.update({"DateStr":{"title":_(u"创角日期")},
"FirstAccIDCount":{"title":_(u"首登人数")},
})
printStr += CommFunc.editTableHtml(loginDrList, loginTDList, styleInfo=loginStyleInfo, printCount=False)
printStr += "
"
printStr += "【%s】
" % _(u"首充留存")
loginStyleInfo.update({"DateStr":{"title":_(u"首充日期")},
"FirstAccIDCount":{"title":_(u"首充人数")},
})
printStr += CommFunc.editTableHtml(ctgLoginDrList, loginTDList, styleInfo=loginStyleInfo, printCount=False)
printStr += "
%s" \
% (eventType, startDate, createRoleEndDate, loginDayList, _(u"查看其他"))
logging.info("show OK")
# 只会返回最后一个print的内容
print printStr
return
def calcLoginDr(firstAccIDInfo, loginDayList, loginDateAccIDInfo):
dateStrList = firstAccIDInfo.keys()
dateStrList.sort()
loginDrList = []
for dateStr in dateStrList:
dateObj = CommFunc.strToDatetime(dateStr)
firstAccIDList = firstAccIDInfo[dateStr]
firstAccIDCount = len(firstAccIDList)
# 留存
loginDrInfo = {"DateStr":dateStr, "FirstAccIDCount":firstAccIDCount}
for loginDay in loginDayList:
loginDateObj = CommFunc.getDiffDaysDate(loginDay - 1, dateObj)
loginDateStr = str(loginDateObj)[:10]
if loginDateStr not in loginDateAccIDInfo:
continue
loginAccIDList = loginDateAccIDInfo[loginDateStr]
loginPlayerCount = 0
for accID in firstAccIDList:
if accID in loginAccIDList:
loginPlayerCount += 1
if loginPlayerCount > 0:
loginRate = loginPlayerCount / float(firstAccIDCount) if firstAccIDCount > 0 else 0
loginRatePer = round(loginRate * 100, 2)
loginDrInfo[loginDay] = "%s%% (%s)" % (loginRatePer, loginPlayerCount)
else:
loginDrInfo[loginDay] = ""
if firstAccIDCount:
loginDrList.append(loginDrInfo)
return loginDrList
def queryCenterBak(startDate, endDate, logInOutDateList, argvDict):
createDateAccIDInfoBak, loginDateAccIDInfoBak, firstCTGRealAccIDInfoBak = {}, {}, {}
if not CommFunc.loopCenterbakRarDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc, parseLineFunc,
createDateAccIDInfoBak, loginDateAccIDInfoBak, logInOutDateList, firstCTGRealAccIDInfoBak, argvDict, drNameList=DRNameList):
return
return CommFunc.queryBackupCenterOK([createDateAccIDInfoBak, loginDateAccIDInfoBak, firstCTGRealAccIDInfoBak])
def checkDrFileNeedParseFunc(drFileName, *parseArgs, **kv):
''' 检查流向是否需要处理
@param drFileName: 流向文件名 xxx_日期.txt
@param *parseArgs: 自定义参数
@return: isNeed, checkNeedParseRetInfo
'''
_, _, logInOutDateList, _, argvDict = parseArgs
if drFileName.startswith("FirstLogin_") or drFileName.startswith("CTGOK_"):
dateStr = drFileName[:drFileName.rindex(".")].split("_")[1]
createRoleStartDate = str(argvDict["startDate"])
createRoleEndDate = str(argvDict["createRoleEndDate"])
if dateStr < createRoleStartDate or dateStr > createRoleEndDate:
return False, None
elif drFileName.startswith("LogInOut_"):
dateStr = drFileName[:drFileName.rindex(".")].split("_")[1]
if dateStr not in logInOutDateList:
return False, None
return True, None
def parseLineFunc(drName, dateStr, checkNeedParseRetInfo, line, *parseArgs, **kv):
''' 解析流向行内容
@param drName: 流向名
@param dateStr: 对应日期字符串
@param checkNeedParseRetInfo: checkDrFileNeedParseFunc 返回的内容
@param line: 本行内容
@param *parseArgs: 自定义参数
'''
createDateAccIDInfo, loginDateAccIDInfo, _, firstCTGRealAccIDInfo, argvDict = parseArgs
serverID = argvDict.get("serverID", "")
sServerID = "@s%s" % serverID
# 首登
if drName == "FirstLogin":
if sServerID not in line:
return
drDict = eval(line)
accID = drDict["AccID"]
if not accID.endswith(sServerID):
return
if dateStr not in createDateAccIDInfo:
createDateAccIDInfo[dateStr] = []
firstLoginAccIDList = createDateAccIDInfo[dateStr]
if accID not in firstLoginAccIDList:
firstLoginAccIDList.append(accID)
# 真实货币首充
elif drName == "CTGOK":
if sServerID not in line:
return
if "ctgRealFirstTime" not in line:
return
drDict = eval(line)
accID = drDict["AccID"]
if not accID.endswith(sServerID):
return
ctgRealFirstDate = drDict["ctgRealFirstTime"][:10]
if ctgRealFirstDate != dateStr:
return
if dateStr not in firstCTGRealAccIDInfo:
firstCTGRealAccIDInfo[dateStr] = []
firstCTGRealAccIDList = firstCTGRealAccIDInfo[dateStr]
if accID not in firstCTGRealAccIDList:
firstCTGRealAccIDList.append(accID)
# 统计登录
elif drName == "LogInOut":
if "logout" in line:
return
if sServerID not in line:
return
if "127.0.0.1" in line:
#logging.info(" 脱机登录,不处理")
return
drDict = eval(line)
if drDict["Type"] != "login":
return
accID = drDict["AccID"]
if not accID.endswith(sServerID):
return
if dateStr not in loginDateAccIDInfo:
loginDateAccIDInfo[dateStr] = []
loginAccIDList = loginDateAccIDInfo[dateStr]
if accID not in loginAccIDList:
loginAccIDList.append(accID)
return
def main():
CommFunc.setdefaultencoding()
argvDict = CommFunc.parse_args()
mylog.InitMyLog(argvDict.get("eventType", ""))
CommFunc.gettextInstall(argvDict.get("lang", ""))
queryServerKeepLoginInfo(argvDict)
return
if __name__ == "__main__":
try:
main()
except:
CommFunc.printExceptionError()