#!/usr/bin/python
|
# -*- coding: utf-8 -*-
|
# @todo: 服务器LTV分析, 仅针对单 serverID 统计,不管合服后
|
|
import CommFunc
|
import ConfigParser
|
import CTGOKSort
|
import mylog
|
import logging
|
|
cfg = ConfigParser.ConfigParser()
|
cfg.read("../../InterfaceConfig.php")
|
ServerPath = cfg.get("ServerInfo", "ServerPath")
|
# 需要处理的流向名及顺序列表
|
DRNameList = ["FirstLogin", "CTGOK"]
|
|
def queryServerLTVInfo(argvDict):
|
logging.info("queryServerLTVInfo %s" % str(argvDict))
|
if "serverID" not in argvDict:
|
print "not serverID"
|
return
|
argvDict["OnlyServerID"] = argvDict["serverID"] # 只查本服
|
|
LVTVPayOrderType = int(CommFunc.getSPConfig(argvDict, "Config", "LVTVPayOrderType", "1"))
|
|
startDate = argvDict.get("startDate", "")
|
endDate = argvDict.get("endDate", "")
|
eventType = argvDict.get("eventType", "")
|
|
ltvDayList = eval(argvDict.get("ltvDayList", "[1, 2, 3, 4, 5, 6, 7]")) # LTV 天(当日)
|
ltvTotalDayList = eval(argvDict.get("ltvTotalDayList", "[7, 14, 15, 21, 30, 45, 60, 75, 90]")) # LTV 天(汇总)
|
maxDay = max(max(ltvDayList), max(ltvTotalDayList))
|
|
createRoleEndDate = argvDict.get("createRoleEndDate", "")
|
if not createRoleEndDate:
|
createRoleEndDate = endDate if endDate else startDate
|
argvDict["createRoleEndDate"] = createRoleEndDate # 创角结束天
|
endDateTime = CommFunc.getDiffDaysDate(maxDay - 1, CommFunc.strToDatetime(createRoleEndDate))
|
endDate = str(endDateTime)[:10]
|
argvDict["endDate"] = endDate
|
logging.info(" upd endDate=%s" % endDate)
|
logging.info(" upd createRoleEndDate=%s" % createRoleEndDate)
|
|
createDateAccIDInfo = {} # {"首登日期":["账号", ...], ...}
|
ctgDateAccIDInfo = {} # {"充值日期":{"账号":orderValue, ...}, ...}
|
|
# 查询中心备份的
|
if CommFunc.isQueryCenterbak(argvDict):
|
queryCenterBak(startDate, endDate, LVTVPayOrderType, argvDict)
|
return
|
|
needQueryCenterbak = CommFunc.loopMainServerDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc, parseLineFunc,
|
createDateAccIDInfo, ctgDateAccIDInfo, LVTVPayOrderType, argvDict, drNameList=DRNameList)
|
logging.info("needQueryCenterbak %s" % needQueryCenterbak)
|
if needQueryCenterbak:
|
bakDataInfo = CommFunc.queryBackupCenterDR(cfg, argvDict)
|
logging.info("queryBackupCenterDR OK")
|
if bakDataInfo == None:
|
return
|
createDateAccIDInfoBak, ctgDateAccIDInfoBak = 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, accIDCtgInfoBak in ctgDateAccIDInfoBak.items():
|
if bakDate not in ctgDateAccIDInfo:
|
ctgDateAccIDInfo[bakDate] = accIDCtgInfoBak
|
else:
|
accIDCtgInfo = ctgDateAccIDInfo[bakDate]
|
for accID, orderValue in accIDCtgInfoBak.items():
|
accIDCtgInfo[accID] = accIDCtgInfo.get(accID, 0) + orderValue
|
|
logging.info("query all data OK")
|
dateStrList = createDateAccIDInfo.keys()
|
dateStrList.sort()
|
|
ltvDrList = []
|
ltvStyleInfo = {}
|
|
ltvTDList = ["DateStr", "FirstLoginCount"]
|
for ltvDay in ltvDayList:
|
ltvTDList.append(ltvDay)
|
ltvStyleInfo[ltvDay] = {"align":"left", "title":_(u"第%s日LTV") % ltvDay}
|
for ltvTotalDay in ltvTotalDayList:
|
key = "%sTotal" % ltvTotalDay
|
ltvTDList.append(key)
|
ltvStyleInfo[key] = {"align":"left", "title":_(u"%s日总LTV") % ltvTotalDay}
|
|
# 最后总LTV
|
LTVTotalDayTDKey = "LTVTotalDay"
|
MaxDayCoinTotalKey = "MaxDayCoinTotal"
|
ltvTDList.extend([LTVTotalDayTDKey, MaxDayCoinTotalKey])
|
ltvStyleInfo[LTVTotalDayTDKey] = {"align":"left", "title":_(u"时段总LTV(天 )")}
|
ltvStyleInfo[MaxDayCoinTotalKey] = {"align":"left", "title":_(u"时段总充值")}
|
|
coinRate = CommFunc.getPayOrderCoinRate(LVTVPayOrderType)
|
if LVTVPayOrderType in CTGOKSort.PayByMoneyOrderTypeList:
|
coinRate = 1.0
|
|
firstLoginAccIDCountTotal, allAccIDCoinTotal = 0, 0
|
curDateTime = CommFunc.getCurrentTime()
|
for dateStr in dateStrList:
|
dateObj = CommFunc.strToDatetime(dateStr)
|
maxDateObj = CommFunc.getDiffDaysDate(maxDay - 1, CommFunc.strToDatetime(dateStr)) # 该创角日期最大需要统计到的充值日期
|
if curDateTime < maxDateObj:
|
passDays = (curDateTime - dateObj).days + 1
|
else:
|
passDays = (maxDateObj - dateObj).days + 1
|
firstLoginAccIDList = createDateAccIDInfo[dateStr]
|
firstLoginCount = len(firstLoginAccIDList)
|
firstLoginAccIDCountTotal += firstLoginCount
|
|
# ltv
|
ltvDrInfo = {"DateStr":dateStr, "FirstLoginCount":firstLoginCount}
|
dayCtgInfo = {} # 转化为天汇总
|
for ltvDate, accIDCtgInfo in ctgDateAccIDInfo.items():
|
ltvDateObj = CommFunc.strToDatetime(ltvDate)
|
if not ltvDateObj:
|
continue
|
ltvDay = (ltvDateObj - dateObj).days + 1 # 0即第一天,所以 + 1
|
if ltvDay > maxDay:
|
# 超过该创角天最大需要统计的ltv天,不处理
|
continue
|
ctgCoin = 0
|
for accID, orderValue in accIDCtgInfo.items():
|
if accID in firstLoginAccIDList:
|
ctgCoin += orderValue
|
if ctgCoin > 0:
|
dayCtgInfo[ltvDay] = ctgCoin
|
|
# ltv - 当日
|
for ltvDay in ltvDayList:
|
if ltvDay not in dayCtgInfo:
|
continue
|
ctgCoin = dayCtgInfo[ltvDay]
|
ltv = (ctgCoin / float(firstLoginCount) / coinRate) if firstLoginCount > 0 else 0
|
ltvDrInfo[ltvDay] = CommFunc.roundInt(ltv, 2)
|
|
# ltv - 汇总
|
for ltvTotalDay in ltvTotalDayList:
|
if ltvTotalDay > passDays:
|
# 未到的汇总天不显示
|
continue
|
|
ctgCoinTotal = 0
|
for ltvDay in range(1, ltvTotalDay + 1):
|
ctgCoinTotal += dayCtgInfo.get(ltvDay, 0)
|
|
if not ctgCoinTotal:
|
continue
|
ltvTotal = (ctgCoinTotal / float(firstLoginCount) / coinRate) if firstLoginCount > 0 else 0
|
key = "%sTotal" % ltvTotalDay
|
ltvDrInfo[key] = CommFunc.roundInt(ltvTotal, 2)
|
|
maxDayCoinTotal = sum(dayCtgInfo.values())
|
allAccIDCoinTotal += maxDayCoinTotal
|
if maxDayCoinTotal > 0:
|
maxDayLTVTotal = (maxDayCoinTotal / float(firstLoginCount) / coinRate) if firstLoginCount > 0 else 0
|
ltvDrInfo[LTVTotalDayTDKey] = "%s (%s%s)" % (CommFunc.roundInt(maxDayLTVTotal, 2), passDays, _(u"天"))
|
ltvDrInfo[MaxDayCoinTotalKey] = CommFunc.roundInt(maxDayCoinTotal / coinRate, 2)
|
|
if firstLoginCount:
|
ltvDrList.append(ltvDrInfo)
|
|
orderTypeName = CommFunc.getPayOrderTypeName(LVTVPayOrderType)
|
|
printStr = "<center><p>%s</p></center>" % _(u"LTV分析")
|
if startDate:
|
printStr += "%s: %s<br/>" % (_(u"查询统计创角开始日期"), startDate)
|
if createRoleEndDate:
|
printStr += "%s: %s<br/>" % (_(u"查询统计创角结束日期"), createRoleEndDate)
|
|
printStr += "%s: %s<br/>" % (_(u"查询总计 LTV天数列表"), ltvTotalDayList)
|
|
# 表格输出
|
printStr += "<br/>【LTV】 %s: %s %s: %s %s<br/>" % (_(u"首登总人数"), firstLoginAccIDCountTotal,
|
_(u"总充值"), str(CommFunc.roundInt(allAccIDCoinTotal / coinRate, 2)), orderTypeName)
|
ltvStyleInfo.update({"DateStr":{"title":_(u"日期")},
|
"FirstLoginCount":{"title":_(u"首登人数")},
|
})
|
printStr += CommFunc.editTableHtml(ltvDrList, ltvTDList, styleInfo=ltvStyleInfo, printCount=False)
|
|
printStr += "<br/><a href='QueryEventData.php?eventType=%s&startDate=%s&endDate=%s<vDayList=%s<vTotalDayList=%s'>%s</a>" \
|
% (eventType, startDate, createRoleEndDate, ltvDayList, ltvTotalDayList, _(u"查看其他"))
|
logging.info("show OK")
|
# 只会返回最后一个print的内容
|
print printStr
|
return
|
|
def queryCenterBak(startDate, endDate, LVTVPayOrderType, argvDict):
|
createDateAccIDInfoBak, ctgDateAccIDInfoBak = {}, {}
|
if not CommFunc.loopCenterbakRarDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc, parseLineFunc,
|
createDateAccIDInfoBak, ctgDateAccIDInfoBak, LVTVPayOrderType, argvDict, drNameList=DRNameList):
|
return
|
|
return CommFunc.queryBackupCenterOK([createDateAccIDInfoBak, ctgDateAccIDInfoBak])
|
|
|
def checkDrFileNeedParseFunc(drFileName, *parseArgs, **kv):
|
''' 检查流向是否需要处理
|
@param drFileName: 流向文件名 xxx_日期.txt
|
@param *parseArgs: 自定义参数
|
@return: isNeed, checkNeedParseRetInfo
|
'''
|
|
_, _, _, argvDict = parseArgs
|
if drFileName.startswith("FirstLogin_"):
|
dateStr = drFileName[:drFileName.rindex(".")].split("_")[1]
|
createRoleStartDate = str(argvDict["startDate"])
|
createRoleEndDate = str(argvDict["createRoleEndDate"])
|
if dateStr < createRoleStartDate or dateStr > createRoleEndDate:
|
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, ctgDateAccIDInfo, LVTVPayOrderType, 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
|
|
drDict = eval(line)
|
accID = drDict["AccID"]
|
if not accID.endswith(sServerID):
|
return
|
|
payOrderType = drDict.get("payOrderType", CTGOKSort.DefaultOrderType)
|
if payOrderType != LVTVPayOrderType:
|
return
|
|
if payOrderType in CTGOKSort.PayByMoneyOrderTypeList:
|
orderValue = drDict["orderMoneyValue"]
|
else:
|
if not CTGOKSort.IsRealCTG(drDict):
|
return
|
orderValue = drDict["orderCoin"]
|
|
if dateStr not in ctgDateAccIDInfo:
|
ctgDateAccIDInfo[dateStr] = {}
|
accIDCtgInfo = ctgDateAccIDInfo[dateStr]
|
accIDCtgInfo[accID] = accIDCtgInfo.get(accID, 0) + orderValue
|
|
return
|
|
def main():
|
CommFunc.setdefaultencoding()
|
argvDict = CommFunc.parse_args()
|
mylog.InitMyLog(argvDict.get("eventType", ""))
|
CommFunc.gettextInstall(argvDict.get("lang", ""))
|
queryServerLTVInfo(argvDict)
|
return
|
|
if __name__ == "__main__":
|
try:
|
main()
|
except:
|
CommFunc.printExceptionError()
|