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