#!/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 = "<center><p>%s</p></center>" % _(u"留存分析")
|
if startDate:
|
printStr += "%s: %s<br/>" % (_(u"查询统计开始日期"), startDate)
|
if createRoleEndDate:
|
printStr += "%s: %s<br/>" % (_(u"查询统计结束日期"), createRoleEndDate)
|
|
printStr += "<br>"
|
# 表格输出
|
printStr += "【%s】<br/>" % _(u"创角留存")
|
loginStyleInfo.update({"DateStr":{"title":_(u"创角日期")},
|
"FirstAccIDCount":{"title":_(u"首登人数")},
|
})
|
printStr += CommFunc.editTableHtml(loginDrList, loginTDList, styleInfo=loginStyleInfo, printCount=False)
|
|
printStr += "<br>"
|
printStr += "【%s】<br/>" % _(u"首充留存")
|
loginStyleInfo.update({"DateStr":{"title":_(u"首充日期")},
|
"FirstAccIDCount":{"title":_(u"首充人数")},
|
})
|
printStr += CommFunc.editTableHtml(ctgLoginDrList, loginTDList, styleInfo=loginStyleInfo, printCount=False)
|
|
printStr += "<br/><a href='QueryEventData.php?eventType=%s&startDate=%s&endDate=%s&loginDayList=%s'>%s</a>" \
|
% (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()
|