| #!/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() |