FANDOM


--[[<pre> Creates an automatically sorted Incubator course table & shows statistics related to it
Debug console code
local frame = {args = { '1',['src']=8, 5 },getParent = function () local pArgs = {'1',['src']=8, 5 }  return { args = pArgs } end } mw.log('')
]]--
local p = {}
local u = require('Module:Utility')
local l = require('Module:Lang')
local tableBuilder = require('Module:Tablebuilder')
local chart = require('Module:Chart')
local mFileLink = require('Module:File link')
local tOrigCourse ={["fren"]=1,["enfr"]=1,["iten"]=1,["enit"]=1,["pten"]=1,["enpt"]=1,["esen"]=1,["enes"]=1,["deen"]=1,["ende"]=1}
local tCourses,tSrcCourses,tFastest,tCrowPrint = {},{},{},{}
local tCrowData = mw.loadData('Module:Crow/data')
local mJsonParser = require("Module:Crow/jsondata")
local tJsonCrow = mJsonParser.getJsonData("Module:Crow/json")
 
-- Adds a new row to the language course table
function p.showcourses(frame)
    local tIncRows      ={}
    local tSortedRows   ={}
    local textsplit  = mw.text.split
    local tCourseCode,sFrom,sTo = "", "",""
    local sP1date, sP2date, sP3date = "", "",""
    local iPhase, phaselink = 0,""
    local hStatRow 
 
    if next(tJsonCrow) then 
       tCrowData = tJsonCrow 
    end
 
    for sCourse,sTableData  in pairs(tCrowData) do
        tCourseCode = textsplit(sCourse,"#") -- find #
        sFrom = tCourseCode[1] -- get L1
        sTo = tCourseCode[2]   -- get L2
        sP1date = sTableData.P1date 
        sP2date = sTableData.P2date 
        sP3date = sTableData.P3date
 
        iPhase, phaselink = p.getcurrphase(sP1date ,sP2date ,sP3date ,sFrom,sTo)
        tIncRows = { l.getLang(sFrom), sFrom,sTo,sP1date, sP2date , sP3date,iPhase
                    ,sTableData.P1ref or "",sTableData.P2ref or "",sTableData.P3ref or "" }
        tSortedRows  = sortedAdd (tIncRows,tSortedRows,7,"desc" )
    end
 
    local hTableCourses =  tableBuilder.createTable("List of courses", "background-color:white",{"wikitable","sortable","crowtable"})
    local hStatRow = tableBuilder.createRow ({["style"]="background-color:white"})
    local mPhase1Link = p.getFileLink('phase1.png','25x30px','Incubator#Phase_1', 'Phase 1')
    local mPhase2Link = p.getFileLink('phase2.png','25x30px','Incubator#Phase_2', 'Phase 2')
    local mPhase3Link = p.getFileLink('phase3.png','25x30px','Incubator#Phase_3', 'Phase 3')
    local mPhasesLink = "" --p.getFileLink('','25x30px',Incubator,"Phases")
    --Adding header columns
    local tSortNumber = {["data-sort-type"]="number"}
    local tSortText = {["data-sort-type"]="text"}
    local tCourseRow = {
    [1]={[1]=tableBuilder.createCol("Languages",{["colspan"]="2"},true),
        [2]=tableBuilder.createCol("Phase",{},true),
        [3]=tableBuilder.createCol("Date♠ entering phase",{["colspan"]="3"},true),
        [4]=tableBuilder.createCol("Days in phase",{["colspan"]="3"},true)
        },
    [2]={
        [1]=tableBuilder.createCol("For (L1)",tSortText,true),
        [2]=tableBuilder.createCol("Teaching",tSortText,true),
        [3]=tableBuilder.createCol(mPhasesLink,tSortNumber,true),
        [4]=tableBuilder.createCol(mPhase1Link,tSortText,true),
        [5]=tableBuilder.createCol(mPhase2Link,tSortText,true),
        [6]=tableBuilder.createCol(mPhase3Link,tSortText,true),
        [7]=tableBuilder.createCol(mPhase1Link,tSortNumber,true),
        [8]=tableBuilder.createCol(mPhase2Link,tSortNumber,true),
        [9]=tableBuilder.createCol(mPhase1Link ..'+'.. mPhase2Link,tSortNumber,true)}
        }
    -- Header columns 
    for iRow, tCells in pairs(tCourseRow) do
        hStatRow = tableBuilder.createRow ({["style"]="background-color:white"})
        hStatRow =tableBuilder.appendColumns(hStatRow,tCells)
        hTableCourses = tableBuilder.appendRow(hTableCourses,hStatRow) 
    end
    for i=1,#tSortedRows  do 
       hStatRow =  p.addrow(tSortedRows[i][2],tSortedRows[i][3],tSortedRows[i][4],tSortedRows[i][5],tSortedRows[i][6],tSortedRows[i][8],tSortedRows[i][9],tSortedRows[i][10]) 
       hTableCourses = tableBuilder.appendRow(hTableCourses,hStatRow) 
    end
    return hTableCourses 
end
 
function p.addrow( sFrom,sTo, sP1Date,sP2Date,sP3Date,sP1ref,sP2ref,sP3ref)
    local tBackColor = {[1] = "#E9E9E9", [2] = "#CCFFCC", [3] = "#AAFFAA"}
    local bisOrig = p.isOrigCourse(sFrom,sTo)   
    local iPhase, sPhaselink = p.getcurrphase(sP1Date,sP2Date,sP3Date,sFrom,sTo)
    local sBackColor = tBackColor[tonumber(iPhase)]
    local sStyle =  'background-color:' .. sBackColor ..'; text-align:center'
    local tCourseRow = {
    [1]=p.flagheader( sFrom)   , -- From flag
    [2]=p.flagheader( sTo) ,       -- to flag
    [3]=tableBuilder.createCol(sPhaselink,{["data-sort-value"]=iPhase}) , 
    [4]=p.frmtdate(sP1Date,sP1ref),
    [5]=p.frmtdate(sP2Date,sP2ref),
    [6]=p.frmtdate(sP3Date,sP3ref),
    [7]=p.days(sP1Date, sP2Date,bisOrig,iPhase),                                         
    [8]=p.days(sP2Date, sP3Date,bisOrig,iPhase), 
    [9]=p.days(sP1Date, sP3Date,bisOrig,iPhase)}
    local hStatRow = tableBuilder.createRow ({["style"]=sStyle})
    tableBuilder.appendColumns(hStatRow,tCourseRow)
 
    return hStatRow
end
 
function p.getcurrphase(p1,p2,p3,fromlangcode,tolangcode)
    local iPhase = 1
    if fromlangcode and tolangcode and (tOrigCourse[fromlangcode..tolangcode]==1) then 
        iPhase = 3  
    elseif u.checkDate(p1) then
        iPhase = 1
        if u.checkDate(p2) then
            iPhase = 2
            if u.checkDate(p3) then
	            iPhase = 3
            end
        end
    end
    if not(p1) and not(p2) and not(p3) and not(fromlangcode) and not(tolangcode) then
        return iPhase 
    end
    local phaselink = '['..'http://incubator.duolingo.com/courses/'..tolangcode..'/'..fromlangcode..'/status'..' '..iPhase..']'
 
    return iPhase, phaselink
end
 
--Shows the actual stats
function p.showstats(frame)
    local tArgs= u.getArgs(frame)
    if not(tArgs) then 
        return
    end
    local sStats= tArgs[1]
    local sStatInfo = p.createStats(sStats) 
    return sStatInfo 
end
 
--Creates stats, can be used by other scripts.
function p.createStats(sStat)
    local tCoursesPhase = {[1]=0,[2]=0,[3]=0}
    local tCrow ={}
    local tRows = {}
    local tTotDays = {["iDaysP1_2"]=0,["iDaysP2_3"]=0,["iDaysP1_3"]=0,
                      ["iCoursesP1_2"]=0,["iCoursesP2_3"] =0,["iCoursesP1_3"] =0}
    local tLang ={["to"]={},["from"]={},["toP23"]={},["fromP23"]={},["toP1"]={},["fromP1"]={}, ["toLIST"]={},["fromLIST"]={},["toP23LIST"]={},["fromP23LIST"]={},["toP1LIST"]={},["fromP1LIST"]={}}
    local textsplit  = mw.text.split
    local tCourseCode,sFrom,sTo = "", "",""
    local sP1date, sP2date, sP3date = "", "",""
    local iPhase, phaselink = 0,""
    local hStatRow 
 
    if next(tJsonCrow) then 
       tCrowData = tJsonCrow 
    end
 
    for sCourse,sTableData  in pairs(tCrowData) do
        tCourseCode = textsplit(sCourse,"#") -- find #
        sFrom = tCourseCode[1] -- get L1
        sTo = tCourseCode[2]     -- get L2
        sP1date = sTableData.P1date 
        sP2date = sTableData.P2date 
        sP3date = sTableData.P3date
        tRows = {sFrom,sTo,"","","",sP1date ,sP2date ,sP3date }
        iPhase, sLink = p.getcurrphase(sP1date ,sP2date,sP3date ,sFrom,sTo)
 
        p.addLangRow(tRows ,tCrow) -- ← IMPORTANT
        if(iPhase) then
	        tCoursesPhase[iPhase]  = tCoursesPhase[iPhase] + 1
        end
        tLang = p.addLang( tLang,sFrom,sTo, iPhase)
    end
    -- CREATE fastest rows
    local tNewestCourses = {["recent"]={["P1"]={},["P2"]={},["P3"]={}},
                            ["new"]={["P1"]={["name"]="",["date"]="0000-00-00"},["P2"]={["name"]=""
                           ,["date"]="0000-00-00"},["P3"]={["name"]="",["date"]="0000-00-00"}}}
    local tAddedOrder = {["p1"]={},["p2"]={},["p3"]={}}
 
    for sCourse,tCourseTable in pairs(tCrow) do
        p.getFastest(tCrow[sCourse],tFastest)    
        for i=1,3 do
            tAddedOrder = addReleasedDate(tCrow[sCourse],i,tAddedOrder,sCourse)
        end
    end
 
    --Gets newest course
    for sCourse,tCourseTable in pairs(tCrowData) do
        p.getNewestCourse(sCourse,tCrowData,tNewestCourses,1)
        p.getNewestCourse(sCourse,tCrowData,tNewestCourses,2)
        p.getNewestCourse(sCourse,tCrowData,tNewestCourses,3)
    end
 
    local iTotCourses = tCoursesPhase[1] + tCoursesPhase[2] + tCoursesPhase[3] 
    local tGraphVals = {["to"]={},["from"]={}, ["toP1"]={},["fromP1"]={}, ["toP23"]={},["fromP23"]={}}
    local _,_ = createLangList(tLang["from"], tLang["fromLIST"],"from",tGraphVals)
    local _,_ = createLangList(tLang["to"], tLang["toLIST"],"to",tGraphVals) -- creates info for graph
    local sLearningP23a, iLearningP23 = createLangList(tLang["toP23"], tLang["toP23LIST"],"toP23",tGraphVals)
    local sLearningP1a, iLearningP1  = createLangList(tLang["toP1"], tLang["toP1LIST"],"toP1",tGraphVals)
    local sTeachingP23a, iTeachingP23 = createLangList(tLang["fromP23"], tLang["fromP23LIST"],"fromP23",tGraphVals)
    local sTeachingP1a, iTeachingP1  = createLangList(tLang["fromP1"], tLang["fromP1LIST"],"fromP1",tGraphVals)
 
    if (sStat== "statmonthlycoursehist") then   
       local sCourseChart =  p.createBarChart(tFastest["Stat"])
       return sCourseChart
    end
 
    if (sStat== "statpietotcourses") then
        return p.createPieChart(tGraphVals["to"], "order=yes")
    end
 
    if (sStat== "2from") then
        return p.createPieChart(tGraphVals["from"], "order=yes")
    end
 
    if (sStat== "2P1to") then
        return p.createPieChart(tGraphVals["toP1"], "order=yes")
    end
 
    if (sStat== "2P1from") then
        return p.createPieChart(tGraphVals["fromP1"], "order=yes")
    end
 
    if (sStat== "2P23to") then
        return p.createPieChart(tGraphVals["toP23"], "order=yes")
    end
 
    if (sStat== "2P23from") then
        return p.createPieChart(tGraphVals["fromP23"], "order=yes")
    end
 
    if (sStat== "statnoofcourses") then
        local hTableCourses =  tableBuilder.createTable("", "background-color:gray;width:100%",{"duotable"})
        local hStatRow = tableBuilder.createRow ({["style"]="background-color:green"})
        local tCourseRow = {
        [1]={[1]=tableBuilder.createCol ("Available",{["style"]="text-align: center; background-color:MediumSeaGreen;",["rowspan"]="2"},true),
            [2]=tableBuilder.createCol ("To learn <br>(".. iLearningP23 .. " languages)",{["style"]="text-align: center; background-color:PaleGreen;font-size:13px;"},true),
            [3]=tableBuilder.createCol (sLearningP23a,{["style"]="text-align: left; background-color:PaleGreen;font-size:11px;"},false)},
        [2]={[1]=tableBuilder.createCol ("For speakers of <br> (".. iTeachingP23 .. " languages)",{["style"]="text-align: center; background-color:LightGreen;font-size:13px;"},true),
            [2]=tableBuilder.createCol (sTeachingP23a,{["style"]="text-align: left; background-color:LightGreen;font-size:11px;"},false)},
        [3]={[1]=tableBuilder.createCol ("Being<br>built",{["style"]="text-align: center; background-color:Gray;",["rowspan"]="2"},true),
            [2]=tableBuilder.createCol ("To learn <br>(".. iLearningP1 .. " languages)",{["style"]="text-align: center; background-color:LightGray;font-size:13px;"},true),
            [3]=tableBuilder.createCol (sLearningP1a,{["style"]="text-align: left; background-color:LightGray;font-size:11px;"},false)},
        [4]={[1]=tableBuilder.createCol ("For speakers of <br>(".. iTeachingP1 .. " languages)",{["style"]="text-align: center; background-color:Silver;font-size:13px;"},true),
            [2]=tableBuilder.createCol (sTeachingP1a,{["style"]="text-align: left; background-color:Silver;font-size:11px;"},false)}}
        local hColHeader =tableBuilder.createCol ("Number of courses per language",{["data-sort-type"]="text",["colspan"]="3"},true)
 
        tableBuilder.appendCol(hStatRow,hColHeader) 
        hTableCourses = tableBuilder.appendRow(hTableCourses,hStatRow) 
 
        for iKey,tColumns in ipairs(tCourseRow) do
            hStatRow = tableBuilder.createRow ({["style"]="text-align:center"})
            for iKey,hCol in ipairs(tColumns) do 
                tableBuilder.appendCol(hStatRow,hCol) 
            end
            hTableCourses = tableBuilder.appendRow(hTableCourses,hStatRow) 
        end
 
        return (hTableCourses)
    end
 
    if (sStat== "statvolcourses") then
        local sTrophy1 =  p.getFileLink('Trophy1.png','20x20px', '','Fastest Course Phase')
        local tCourseRow = {
            {"","Phase 1","Phase 2","Phase 3"},
            {"No. of Courses (".. iTotCourses .. ")",tCoursesPhase[1],tCoursesPhase[2],tCoursesPhase[3]},
            {"Duration in days","in Phase 1","in Phase 2","To Phase 3"},
            {"Average per phase",tFastest["1_2"]["tAvg"]["iAvg"],tFastest["2_3"]["tAvg"]["iAvg"],tFastest["1_3"]["tAvg"]["iAvg"]},
            {"Course moved to phase (average days)",calcAvgReleaseInterval(1,tAddedOrder),calcAvgReleaseInterval(2,tAddedOrder),calcAvgReleaseInterval(3,tAddedOrder)},
            {"Fastest",sTrophy1,sTrophy1,sTrophy1},
            {"Original Course",p.createFastRow(tFastest,"tOrig","1_2"),p.createFastRow(tFastest,"tOrig","2_3"),p.createFastRow(tFastest,"tOrig","1_3")},
            {"Reverse Course",p.createFastRow(tFastest,"tRev","1_2"),p.createFastRow(tFastest,"tRev","2_3"),p.createFastRow(tFastest,"tRev","1_3")},
            {"New tree",p.createFastRow(tFastest,"tNew","1_2"),p.createFastRow(tFastest,"tNew","2_3"),p.createFastRow(tFastest,"tNew","1_3")},
            {"New source language",p.createFastRow(tFastest,"tNewSrc","1_2"),p.createFastRow(tFastest,"tNewSrc","2_3"),p.createFastRow(tFastest,"tNewSrc","1_3")}
        }
        local hTableCourses = tableBuilder.new(tCourseRow,"Volunteer Courses Statistics", "width:100%",{"duotable"}) 
 
        for iRow=1,#tCourseRow do
            hTableCourses:setCellHeader(iRow,1,true)
            for i=1,4 do
                if iRow==3 or iRow==6  then
                    hTableCourses:setCellHeader(iRow,i,true)
                end
                hTableCourses:setCellAttr(iRow,i,{["style"]="text-align: center"})
            end
        end
 
        return hTableCourses:getTable()
    end
 
    if (sStat == "getnewbeta") then
        local sCurrDate =os.date('%Y-%m-%d')
        local tNewData = {  [1] ={"P2","Newest Duolingo course(s): ",''},
                            [2] ={"P1","Recently added to the Incubator:",""},
                            [3] ={"P3","Recently graduated from beta:",""}}
        local sTmp =""
        local sPdate, sPrefix, sSuffix 
        local tCurrNew = {}
        local sTotDays = ""
        for i=1,3 do
            sPdate  = tNewData[i][1]
            sPrefix = tNewData[i][2]
            sSuffix = tNewData[i][3]
            tCurrNew = tNewestCourses["recent"][sPdate]
 
            if next(tCurrNew) then
                sTmp =sTmp..sPrefix .."\r\n"
                for i=0,#tCurrNew do
                    if (tCurrNew[i] and tCurrNew[i].date and calcDaysBetween(tCurrNew[i].date,sCurrDate)<8) then
                        if (tCurrNew[i].iDaysTaken and tCurrNew[i].iDaysTaken > 0 and i>1) then
                            sTotDays = '('..tCurrNew[i].date..', Days:'..tCurrNew[i].iDaysTaken..')'
                        end
 
                        sTmp = sTmp.."*"..' '..tCurrNew[i].sDesc..' '.. sSuffix ..sTotDays.."\r\n"
                    end
                end
            end
        end
        return p.preprocess(sTmp)
    end
 
    if (sStat == "yearstats") then
        local hTable = mw.html.create("table"):addClass("duotable"):css("width","100%")
        local year, rowNode
        local currentYear = tonumber(os.date("%Y"))
        local courses = {[currentYear] ={},[currentYear-1] = {}}
        local tmpCourses
 
        hTable:tag("caption"):wikitext("Courses added per year")
        hTable:tag("th"):wikitext("Year")
        hTable:tag("th"):wikitext("Courses")
 
        for courseName,v in pairs(tCourses) do
            year = string.match(tCourses[courseName].P2date,"(%d%d%d%d)-")
            year = tonumber(year)
            if courses[year]  then
                table.insert(courses[year],getFlag(courseName))
            end
        end
 
        for year,courses in pairs(courses) do
            tmpCourses = table.concat(courses, " • ")
            rowNode = mw.html.create("tr")
            rowNode:tag("td")
                :wikitext(year)
                    :css("text-align","center")
            rowNode:tag("td")
                :wikitext(tmpCourses)
                    :css("text-align","center")
            hTable:node(rowNode)
        end
 
        return hTable
    end
end
 
function addReleasedDate(tCourse,iPhase,tAddedOrder,sCourse)
    if (tCourse["P"..iPhase .."date"]) then
        local sPDate = tCourse["P"..iPhase .."date"]
        local sCourse = string.gsub(sCourse,"#","")
        if not(tOrigCourse[sCourse]) then
            if (sPDate) then
                local sYearMon = string.sub(sPDate,1,10) 
                table.insert(tAddedOrder["p"..iPhase],{[sYearMon] = sCourse})
            end
        end
    end
    return tAddedOrder
end
 
function calcAvgReleaseInterval(iPhase,tAddedOrder)
    tAddedOrder = tAddedOrder["p"..iPhase]
 
    if (tAddedOrder[1]) then
        table.sort(tAddedOrder, function(a,b) 
            local sDate,sCourse= next(a); sDate2,sCourse2 =next(b) 
            return sDate < sDate2 
        end)
 
    local sCurrDate = next(tAddedOrder[1])
    local iDays = 0
    local iTotCourses = #(tAddedOrder)
    local nextDate
 
    for i=2,iTotCourses do
        nextDate = next(tAddedOrder[i])
        iDays = iDays + calcDaysBetween(sCurrDate,nextDate)
        sCurrDate = next(tAddedOrder[i])
    end
 
    return  u.round( iDays / iTotCourses)
    end
end
 
function createLangList(tLangList, tLangListdetail, sKey,tGraphVals)
    local sCourse,iCountCourse = "",0
    local Counter_added_lang=0
    local add_v=""
    local start_line="" 
    local sortedLangList = u.spairs(tLangList, function(t,a,b) return t[b] < t[a] end)
 
    for sLangCode,iCourses in sortedLangList do
        iCountCourse = iCountCourse + 1
        table.sort(tLangListdetail[sLangCode], function(a,b) return string.lower(a) < string.lower(b) end)
        add_v='<span title="'..table.concat(tLangListdetail[sLangCode], ", ")..'">('
        if (iCourses<10) then add_v=add_v..'0' end
        add_v=add_v..iCourses..')</span>'
	    if sCourse=="" then
            start_line = ""
            Counter_added_lang=1
	    elseif(Counter_added_lang<10) then
            start_line = "  •  "
            Counter_added_lang=Counter_added_lang+1
	    else
            start_line = "<br/>"
            Counter_added_lang=1
	    end
        sCourse = sCourse..start_line..getFlag(sLangCode)..add_v
 
--      ##For each language to have one color unique in four pie charts.
        local sLang = l.getLang(sLangCode)
        if(not(tGraphVals["from"][sLang])) then tGraphVals["from"][sLang]=0 end
        if(not(tGraphVals["to"][sLang])) then tGraphVals["to"][sLang]=0 end
        if(not(tGraphVals["fromP23"][sLang])) then tGraphVals["fromP23"][sLang]=0 end
        if(not(tGraphVals["toP23"][sLang])) then tGraphVals["toP23"][sLang]=0 end
        if(not(tGraphVals["fromP1"][sLang])) then tGraphVals["fromP1"][sLang]=0 end
        if(not(tGraphVals["toP1"][sLang])) then tGraphVals["toP1"][sLang]=0 end
        tGraphVals[sKey][sLang] = iCourses
    end
    return sCourse,iCountCourse
end
 
--ADD a new languagerow to a Course table
function p.addLangRow(t,tCrow)
    local from, to = t[1], t[2]
    local sKey     = from .. to --→ Original Course
    local sRev = to..from       --→ Reverse Course
    local bIsRev,bHasRev = false,false
 
    tCrow[sKey] = {["from"]=t[1],["to"]=t[2],["web"]=t[3],["android"]=t[4], ["ios"]=t[5],
                   ["P1date"]=p.Date(t[6]),["P2date"]=p.Date(t[7]),["P3date"]=p.Date(t[8]), ["bIsRev"] = bIsRev,
                   ["bHasRev"]= bHasRev, ["bStaff"]=p.isOrigCourse(from,to)}
    local sP1  = tCrow[sKey]["P1date"]
    local sP2  = tCrow[sKey]["P2date"]
    local sP3  = tCrow[sKey]["P3date"]
    local tNewTreeDetails = {["P1date"]=sP1,["P2date"]=sP2,["P3date"]=sP3,["sName"]=from..to}
    local bOldestCourse 
 
    if not(tCourses[to]) and sP2 then 
        tCourses[to] = tNewTreeDetails 
    end
 
    if not(tSrcCourses[from]) and sP2 then 
        tSrcCourses[from] = tNewTreeDetails 
    end
 
    if sP2 and tCourses[to] and tCourses[to]["P2date"] then
       bOldestCourse = u.getdate(tCourses[to]["P2date"]) > u.getdate(sP2)-- Check which tree was created first
       if (bOldestCourse)  then 
          tCourses[to] = tNewTreeDetails
       end
    end
 
    if sP2 and tSrcCourses[from] and tSrcCourses[from]["P2date"] then
       bOldestCourse = u.getdate(tSrcCourses[from]["P2date"]) > u.getdate(sP2)-- Check which tree was created first
       if (bOldestCourse)  then 
          tSrcCourses[from] = tNewTreeDetails
       end
    end
 
    if (tCrow[sRev])   then 
        if (u.checkDate(tCrow[sRev]["P1date"]) and u.checkDate(t[6])) then
            local sDate1 = u.getdate(tCrow[sRev]["P1date"])
            local sDate2 = u.getdate(sP1)
 
            if sDate1 and sDate2 then
                if sDate2 > sDate1 then
                    tCrow[sKey]["bIsRev"] = true       
                else
                    tCrow[sRev]["bIsRev"]= true   
                end
            end
        end
        tCrow[sRev]["bHasRev"] = true  
        tCrow[sKey]["bHasRev"] = true    
    end 
end
 
function p.getNewestCourse(sCourse,tCrow,tNewestCourses,iPhase) 
    local sPhase= "P"..iPhase
    local sDate = "P"..iPhase.."date"
    local s,e  = string.find(sCourse,"#") -- find 
    local sFrom = string.sub(sCourse,1,s-1) -- get L1
    local sTo = string.sub(sCourse,s+1)     -- get L2
    local sCurrDate =os.date('%Y-%m-%d')
 
    if u.getdate(tCrow[sCourse][sDate]) and calcDaysBetween(tCrow[sCourse][sDate],sCurrDate) < 8 then
        local sCourseDesc =  l.getLang(sTo).. ' for '..l.getLang(sFrom).." speakers"
        local sLink = '['..'http://incubator.duolingo.com/courses/'..sTo..'/'..sFrom..'/status '..sCourseDesc..']'
        local sDate1 = "P"..(iPhase).."date"
        if iPhase >1 then
            sDate1 = "P"..(iPhase-1).."date"
        end
        local iTotDays = calcDaysBetween(tCrow[sCourse][sDate1],tCrow[sCourse][sDate])
        if iPhase==1 then
            iTotDays =0
        end
        table.insert(tNewestCourses ["recent"][sPhase],{["date"] = tCrow[sCourse][sDate] , name=sCourse, sDesc = sLink, iDaysTaken =iTotDays })
    end
 
    if u.getdate(tCrow[sCourse][sDate ]) and tCrow[sCourse][sDate] > tNewestCourses ["new"][sPhase]["date"] then
        tNewestCourses ["new"][sPhase] = {["date"] = tCrow[sCourse][sDate], name=sCourse }
    end
end
 
function p.getFastCourse(iPhaseA,iPhaseB,tRows,tFastest)
    local sFrom= tRows["from"]
    local sTo= tRows["to"]
    local sLaunch = tRows["P1date"]
    local bIsOrig = p.isOrigCourse(sFrom,sTo)
    local sFromFlag = getFlag(sFrom)
    local sToFlag =   getFlag(sTo)
    local sPhase = iPhaseA.."_"..iPhaseB
    local sP1  = p.Date(tRows["P"..iPhaseA.."date"])
    local sP2  = p.Date(tRows["P"..iPhaseB.."date"])
    local iDays =calcDaysBetween(sP1,sP2) -- Date1 - Date2
 
    if not(tFastest[sPhase]) then 
        tFastest[sPhase] ={tNewSrc ={["iDays"]=9999},tNew ={["iDays"]=9999}, tAvg={["iDays"] =0,["iCourses"]=0},tOrig={["iDays"] = 9999},tRev={["iDays"] = 9999}}  
    end
 
    if iDays then
        local tDetails ={["sName"]=sFrom..sTo,["iDays"]=u.round(iDays), ["sFlags"] = sFromFlag..""..sToFlag, ["sLaunch"] = sLaunch,[sFrom..sTo] = 1, ["bIsRev"]=tRows["bIsRev"]} 
 
        if not(tRows["bStaff"]) then ---← ←  don't add staff courses    
            local iCalcNewDays = tFastest[sPhase]["tNew"]["iDays"] or 9999
 
            if (tCourses[sTo]["sName"]==sFrom..sTo) and iCalcNewDays > iDays then
                local tOrigLang ={["fr"]=1,["en"]=1,["it"]=1,["pt"]=1,["es"]=1,["de"]=1}
                if not(tOrigLang[sTo]) then
                    tFastest[sPhase]["tNew"]=tDetails
                end
            end
            local iCalcSrcDays = tFastest[sPhase]["tNewSrc"]["iDays"] or 9999
 
            if (tSrcCourses[sFrom]["sName"]==sFrom..sTo) and iCalcSrcDays > iDays then
                local tOrigLang ={["fr"]=1,["en"]=1,["it"]=1,["pt"]=1,["es"]=1,["de"]=1}
                if not(tOrigLang[sFrom]) then
                    tFastest[sPhase]["tNewSrc"]=tDetails
                end
            end
            local iTotDays = calcDaysBetween(sP1,sP2)
 
            if (tFastest[sPhase]["tAvg"] and iTotDays) then 
                local iCDays = tFastest[sPhase]["tAvg"]["iDays"]  + iTotDays
                local iCourses = tFastest[sPhase]["tAvg"]["iCourses"] + 1
                tFastest[sPhase]["tAvg"]["iDays"] =  iCDays
                tFastest[sPhase]["tAvg"]["iCourses"] =  iCourses 
                tFastest[sPhase]["tAvg"]["iAvg"] = u.round(iCDays/ iCourses)
            end
 
            if (tRows["bIsRev"]) then 
                if tFastest[sPhase]["tRev"]["iDays"] > iDays then
                    tFastest[sPhase]["tRev"]= tDetails      
                end
            end
 
            if  not(tRows["bIsRev"]) and tFastest[sPhase]["tOrig"]["iDays"] > iDays then
                tFastest[sPhase]["tOrig"]= tDetails
            end
        end
    end
end
 
function p.addMonthStat(tFastest,sDate,iPhase)
    local sYearMon =""
    if( u.checkDate(sDate) ) then   
        local tTmp = tFastest["Stat"]
        sYearMon = string.sub(sDate,1,7) 
        if not(tFastest["Stat"]) then 
            tFastest["Stat"] = {} tTmp =tFastest["Stat"]   
        end
        if not(tTmp[sYearMon]) then  
            tTmp[sYearMon]={[iPhase]= 0}  
        end
        if not(tTmp[sYearMon][iPhase]) then  
            tTmp[sYearMon][iPhase]= 0  
        end
        tTmp[sYearMon][iPhase] = tTmp[sYearMon][iPhase] + 1
    end
end
 
function p.createFastRow(tFast,sType,sPhases)
    local tOrder = {["1_2"]="",["2_3"]="",["1_3"]=""}
 
    if tFast[sPhases] and tFast[sPhases][sType] and tFast[sPhases][sType]["iDays"] then 
        tOrder[sPhases] =tFast[sPhases][sType]["sFlags"].." ("..tFast[sPhases][sType]["iDays"]..")"
    end
 
    return tOrder[sPhases]
end
 
function p.createBarChart(tPhases )
    local sPhase1,sPhase2,sPhase3,sLegend,sSep="","","","",""
    local sCurrDate =os.date('%Y-%m-%d')
    local sPhaseDate =""
    for sKey,sVal in  u.spairs(tPhases) do
        sPhaseDate = sKey ..'-01'
        if(calcDaysBetween(sCurrDate,sPhaseDate)<180) then 
            sPhase1 = sPhase1..sSep ..(tPhases[sKey][1] or 0)
            sPhase2 = sPhase2..sSep ..(tPhases[sKey][2] or 0)
            sPhase3 = sPhase3..sSep ..(tPhases[sKey][3] or 0)
            sLegend = sLegend..sSep ..sKey
            if sSep == "" then
                sSep = ' : '
            end
        end
    end
    local tChartOptions = {args = {
        ["height"]="300",["width"]="600",
        ["group 1"]=sPhase1,["group 2"]=sPhase2,["group 3"]=sPhase3,
        ["units suffix"] ="_Courses",["x legends"]=sLegend,
        ["colors"]="red : yellow : green",
        ["group names"]="Phase 1 : Phase 2 : Phase 3"}}
    local sChart = chart["bar chart"](tChartOptions)
 
    return sChart
end
 
function p.createPieChart(tSlices, order )
    -- Sort tGraphVals alphabetically with rspect to indexes (which are the language names)
    local tSlicesLOCAL = {}
 
    for sKey,sVal in pairs(tSlices) do
        table.insert(tSlicesLOCAL,{name=sKey, value=sVal})
    end
 
    if(order) then
        table.sort(tSlicesLOCAL,function(a,b) return string.lower(a.name) < string.lower(b.name) end)
    end
    local sSlices = ""
    local k, j, u= 0,0,0
 
    for i=1,#tSlicesLOCAL do
        sSlices = sSlices..'('..tSlicesLOCAL[i].value..':'..tSlicesLOCAL[i].name
        if(i>26) then
            if(k==0) then
                sSlices = sSlices..':#DD'
            elseif(k==1) then
                sSlices = sSlices..':#99'
            elseif(k==2) then
                sSlices = sSlices..':#66'
            elseif(k==3) then
                sSlices = sSlices..':#22'
            end
            if(j<10) then
                sSlices = sSlices..j..j
            elseif(j==10) then
                sSlices = sSlices..'AA'
            elseif(j==11) then
                sSlices = sSlices..'BB'
            elseif(j==12) then
                sSlices = sSlices..'CC'
            elseif(j==13) then
                sSlices = sSlices..'DD'
            elseif(j==14) then
                sSlices = sSlices..'EE'
            elseif(j==15) then
                sSlices = sSlices..'FF'
            end
            if(k==3) then
                k=-1
                j=j+1
            end
            k=k+1
            u=i
            while(u>99)do u=u-100 end
            sSlices = sSlices..u
        end
        sSlices = sSlices .. ')'
    end
    local tChartOptions = {args = {["radius"]="145",["hide group legends"]="true",
        ["slices"]=sSlices,["units suffix"] ="_Courses",["percent"]="true"}}
    local sChart = chart["pie chart"](tChartOptions)
 
    return sChart
end
 
--Adds values in sorted order using a column for comparison
--Params (tTable - Original table, tSortedRows - sorted table,iSortField - column/field to sort (num),order- "asc" or "desc" 
 
function sortedAdd (tTable, tSortedRows,iSortField,order)
    local bAddedFlag = false
 
    local function sortedInsert(i)
        table.insert(tSortedRows,i,tTable)
        bAddedFlag = true
    end 
 
    for i=1,#tSortedRows  do 
        if (tSortedRows[i] and tSortedRows[i][iSortField] and tTable[iSortField]) then
            if  (tSortedRows[i][iSortField] > tTable[iSortField]) and order == "asc" then
                sortedInsert(i)
            break
            elseif (tSortedRows[i] and (tSortedRows[i][iSortField] < tTable[iSortField])) and order == "desc" then
                sortedInsert(i)
            break
            elseif (tSortedRows[i] and (tSortedRows[i][iSortField] == tTable[iSortField]))  then
                if tSortedRows[i][1] > tTable[1] then
                    sortedInsert(i)
                break
                end
            end
        end
    end
 
    if not(bAddedFlag) then 
        table.insert(tSortedRows,tTable)
    end
    return tSortedRows
end
 
function p.flagheader( sLangCode)
    local sLangName = l.getLang(sLangCode)
    local sLangName =string.gsub(sLangName,"(%b())","")
    local sHeader= getFlag(sLangCode,'15x15px')..sLangName
 
    return tableBuilder.createCol(sHeader,{["data-sort-value"]=string.lower(sLangName),["style"]="text-align:left"})
end
 
function p.getFileLink(sFileName,sSize,sLink,sCaption,sAlt)
    local sFileLink = mFileLink.main{	file = sFileName,size = sSize,link = sLink,caption = sCaption,alt=sAlt}
 
    return sFileLink
end
 
function p.frmtdate( sDate1,sRef)
    if not(sDate1) then 
        sDate1 =""
    end
    local frmDate = sDate1
 
    if not(u.checkDate(sDate1)) then
        frmDate='0000-01-01'
    end
 
    local _,_,bEst,z = string.find(sDate1,"(%(%est%.%))")
    local _,_,bBef,z = string.find(sDate1,"(%(%bef%.%))")
 
    if bEst or bBef then 
        sDate1 = p.preprocess("''"..sDate1.."''")  
    end
 
    if (sDate1 =="0000-00-00") then
        sDate1 = "(Pre-incubator)"
    end
    sDate1 = p.preprocess(sDate1 .. ""..sRef)
 
    return tableBuilder.createCol(sDate1,{["style"]="font-size:70%",["data-sort-value"]=string.sub(frmDate,0,10)})
end
 
function p.days( sDate1, sDate2,bOrig,iPhase) 
 
    local iDays,iDaysSORT = 0,0
    local sEstimate, sSuffixEstimate, sSortValue ="", "", ""
 
    if (sDate1) and (sDate2) then 
        if bOrig then
            sSuffixEstimate = p.preprocess("<span title='estimate'>ˠ</span>") 
        end 
    end
    local sDate1LEN, sDate2LEN = 1,1
 
    if (sDate1) then 
        sDate1LEN = math.min(string.len(sDate1),10) 
    end
 
    if (sDate2) then 
        sDate2LEN = math.min(string.len(sDate2),10) 
    end
 
    if not(sDate1) or (sDate1=="") then
        iDaysSORT=0
    else
        if not(sDate2) or (sDate2 =="") then
            sDate2 = os.date('%Y-%m-%d')
            sSuffixEstimate="<span title='Elapsed days (Phase not yet reached)'>*</span>"
            iDaysSORT=999
 
        elseif string.sub(sDate2,1,sDate2LEN)=='2013-05' then
        -- So it's (original) EN<-PT course
            sDate2=p.Date('(2013-05-01)')
        end
 
        if string.sub(sDate1,1,sDate1LEN)=='2011' then
            sDate1=p.Date('(2011-12-01)')
        elseif string.sub(sDate1,1,sDate1LEN)=='2012' then
            sDate1=p.Date('(2012-06-01)')
        end
 
        if string.sub(sDate1,1,sDate1LEN)=='0000-00-00' or string.sub(sDate2,1,sDate2LEN)=='0000-00-00' then
            iDays=""
            sSuffixEstimate = ""
        else 
            iDays = (u.datediff(sDate1,sDate2))
            iDaysSORT=iDaysSORT..iDays
        end
    end
 
    if iDays == 0 then 
        iDays ="" 
        sSuffixEstimate = ""
    end
    local sDateString = iDays..sSuffixEstimate
 
    return tableBuilder.createCol(sDateString,{["data-sort-value"]=iDaysSORT})
end
 
function p.getFastest(tRows,tFastest)
    p.getFastCourse(1,2,tRows,tFastest)
    p.getFastCourse(2,3,tRows,tFastest)
    p.getFastCourse(1,3,tRows,tFastest)
    -- Add monthly chart
    p.addMonthStat(tFastest,tRows["P1date"],1)
    p.addMonthStat(tFastest,tRows["P2date"],2)
    p.addMonthStat(tFastest,tRows["P3date"],3)
    --end
    return tFastest
end
 
function getFlag(sLangCode,sSize)
    local sLangName = l.getLang(sLangCode)
    sSize = sSize  or '20x20px'
    local sHeaderFlagLink = p.getFileLink('Flag-'.. sLangCode..'.png',sSize,sLangName,sLangName,sLangCode)
    return sHeaderFlagLink
end
 
function p.addLang( tLang,sFrom,sTo, iPhase)
    if not(tLang) then 
       return 
    end
 
    local loc_index=0
    local sFromUppered, sToUppered=sFrom,sTo
 
    if(string.len(sFrom)==2) then 
        sFromUppered=string.upper(sFrom) 
    end
 
    if(string.len(sTo)==2) then 
        sToUppered=string.upper(sTo) 
    end
 
    tLang["to"][sTo] = (tLang["to"][sTo] or 0) + 1
    if(tLang["toLIST"][sTo]) then
        tLang["toLIST"][sTo][(#tLang["toLIST"][sTo] or 0)+1]=sFromUppered
    else
        tLang["toLIST"][sTo]={}
        tLang["toLIST"][sTo][1]=sFromUppered
    end
    tLang["from"][sFrom] = (tLang["from"][sFrom] or 0) + 1
 
    if(tLang["fromLIST"][sFrom]) then
        tLang["fromLIST"][sFrom][(#tLang["fromLIST"][sFrom] or 0)+1]=sToUppered
    else
        tLang["fromLIST"][sFrom]={}
        tLang["fromLIST"][sFrom][1]=sToUppered
    end
 
    if (iPhase==1) then
        tLang["toP1"][sTo] = (tLang["toP1"][sTo] or 0) + 1
        if(tLang["toP1LIST"][sTo]) then
            tLang["toP1LIST"][sTo][(#tLang["toP1LIST"][sTo] or 0)+1]=sFromUppered
        else
            tLang["toP1LIST"][sTo]={}
            tLang["toP1LIST"][sTo][1]= sFromUppered
        end
 
        tLang["fromP1"][sFrom] = (tLang["fromP1"][sFrom] or 0) + 1
        if(tLang["fromP1LIST"][sFrom]) then
            tLang["fromP1LIST"][sFrom][(#tLang["fromP1LIST"][sFrom] or 0)+1]=sToUppered
        else
            tLang["fromP1LIST"][sFrom]={}
            tLang["fromP1LIST"][sFrom][1]=sToUppered
        end
    else
        tLang["toP23"][sTo] = (tLang["toP23"][sTo] or 0) + 1
        if(tLang["toP23LIST"][sTo]) then
            tLang["toP23LIST"][sTo][(#tLang["toP23LIST"][sTo] or 0)+1]=sFromUppered
        else
            tLang["toP23LIST"][sTo]={}
            tLang["toP23LIST"][sTo][1]=sFromUppered
        end
 
        tLang["fromP23"][sFrom] = (tLang["fromP23"][sFrom] or 0) + 1
        if(tLang["fromP23LIST"][sFrom]) then
            tLang["fromP23LIST"][sFrom][(#tLang["fromP23LIST"][sFrom] or 0)+1]=sToUppered
        else
            tLang["fromP23LIST"][sFrom]={}
            tLang["fromP23LIST"][sFrom][1]=sToUppered
        end
    end 
    return tLang
end
 
function calcDaysBetween(date1,date2,bNeg)
    local days = nil
    if u.checkDate(date1) and u.checkDate(date2) then 
        days  =  u.datediff(date1,date2,bNeg)
    end 
    return days
end
 
function p.isOrigCourse(from,to)
    local bOrigCourse = (tOrigCourse[from..to]==1)
    return bOrigCourse
end
 
function p.preprocess(sString)
    if (mw.getCurrentFrame()) then 
        return mw.getCurrentFrame():preprocess(sString)
    end
    return sString
end
 
function p.Date(sDate)
    if not(sDate) then 
        return 
    end
    return sDate:match("(%d%d%d%d%-%d%d%-%d%d)")
end
 
return p
--</nowiki>