• [Lua] IT技术熟练度生成器 | 根据活动记录生成md表格 | 自创


    IT技术熟练度 v1.1

    为衡量个人能力水平自创的一套评分机制,根据时间、代码行数、基础理论三个变量生成。最近在学lua,正好练下基本功。效果可见 个人介绍 | 代码统计 - 小能日记 - 博客园 (cnblogs.com)

    life.lua 记录自己每日的IT活动,main.lua 程序根据life.lua生成文件 output.md

    更新

    • 2023年8月24日:条目总学习天数与敲代码天数分开计算,详细见完整代码 main.lua

    具体规则

    • 某一条目为A对象实例(如Lua),初始化40分,范围 (0,100]

      • [0,20) 遗忘、[20,40) 生疏、[40,60) 了解、[60,80) 熟悉、[80,100] 熟练
    • 时间:每隔1天,进行如下模拟遗忘操作,从熟练到遗忘需要90天不敲代码

      • 小于等于80分的条目减1分
      • 小于等于100分的条目减2分
    • 代码行数:根据每天敲的代码有效行数(不含空行)对熟练度增减。

      • 动态计算每个条目平均有效行AVG(4天敲800行得200),不足4天默认按500行计算
      • 当天有效行数处于 [AVG * 1.25,∞] ,加4分
      • 当天有效行数处于 [AVG * 0.75,AVG * 1.25] ,加3分
      • 当天有效行数处于 [AVG * 0.25,AVG * 0.75) ,加2分
      • 当天有效行数处于 [AVG * 0.10,AVG * 0.25) ,加1分
      • 当天有效行数处于 [AVG * 0.00,AVG * 0.10) ,加0分
      • 从50分到80分需要连续敲15天,从80分到100分需要连续敲20天。
      • 越熟练代码越精简,行数逐渐变少,这是量变到质变的过程,故 AVG * 0.75
    • 基础理论:当天涉及基础理论(如网络、算法、设计模式、架构技巧等)

      • 集合元素个数 * 2,如["算法"]:{"马尔科夫链","迪杰斯特拉"},共2个元素,加4分
    • 状态:状态根据当天敲的所有行数比较 AVG = (一生敲的总行数) // (总天数),不足4天默认按500行计算

      • 加班狂魔:[1.25 * AVG,∞]、正常上班:[AVG * 0.50,AVG * 1.25]、天天摸鱼:[AVG * 10,AVG * 0.50 ]
      • 其他与没敲代码的日子均统计到"摆烂"中。学了理论不敲等于没学

    示例效果

    Time : 2023/8/13 ~ 2023/8/23

    技术 学习天数 熟练度 (0~100) 程度 评级
    operation system 3 70 熟悉 ⭐⭐⭐⭐
    Love Engine 2 64 熟悉 ⭐⭐⭐⭐
    lua 10 58 了解 ⭐⭐⭐
    C++ 1 41 了解 ⭐⭐⭐
    algorithm 1 38 生疏 ⭐⭐

    这一生写过的代码

    我的一生 敲的总行数 敲了几天 平均每天敲几行
    C++ 228 1 228
    lua 4629 9 514

    这一生所处的状态

    我的状态 天数
    加班狂魔 2
    正常上班 6
    天天摸鱼 2
    开摆去咯 1

    IT活动记录 life.lua

    lua
    -- 文件 life.lua 格式如下 
    life = {{
        ["date"] = "2023年8月23日",
        ["operation system"] = {"调度器", "六种调度算法"}, -- 涉及理论
        ["C++"] = {228, 0} -- 敲的有效行/注释行
    }, {
        ["date"] = "2023年8月22日",
        ["lua"] = {"map、filter、reduce", "3.01~3.05"}
    }, {
        ["date"] = "2023年8月21日",
        ["lua"] = {585, 55},
        ["Love Engine"] = {"2.16", 2.17, "⭐2.18"}
    }, {
        ["date"] = "2023年8月20日",
        ["lua"] = {445 + 498, 25 + 60},
        ["Love Engine"] = {'2.1', 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 2.10, '打砖块2.11~2.15'}
    }, {
        ["date"] = "2023年8月19日",
        ["lua"] = {238, 9},
        ["operation system"] = {"进程概念、组成、特征", "进程状态与转换", "进程控制",
                                "进程间通信", "线程实现方式", "多线程模型",
                                "线程状态与转换、组织控制"}
    }, {
        ["date"] = "2023年8月18日",
        ["lua"] = {92, 2},
        ["operation system"] = {"概念、功能和目标", "四个特征", "发展和分类", "运行机制",
                                "中断和异常", "系统调用", "体系结构", "引导", "虚拟机"}
    }, {
        ["date"] = "2023年8月17日",
        ["lua"] = {327, 15}
    }, {
        ["date"] = "2023年8月16日",
        ["lua"] = {484, 52},
        ["algorithm"] = {"马尔科夫链", "迪杰斯特拉", "位图法"}
    }, {
        ["date"] = "2023年8月15日",
        ["lua"] = {477, 18}
    }, {
        ["date"] = "2023年8月14日",
        ["lua"] = {519, 29}
    }, {
        ["date"] = "2023年8月13日",
        ["lua"] = {628, 71}
    }}
    

    输出熟练度 output.md

    lua
    (如果有前置head.md则会在这拼接)
    **Time : 2023/8/13 ~ 2023/8/23**
    |技术|学习天数|熟练度 (0~100)|程度|评级|
    |------|----|----|----|----|
    |operation system|3|70|熟悉|⭐⭐⭐⭐|
    |Love Engine|2|64|熟悉|⭐⭐⭐⭐|
    |lua|10|58|了解|⭐⭐⭐|
    |C++|1|41|了解|⭐⭐⭐|
    |algorithm|1|38|生疏|⭐⭐|
    
    # 这一生写过的代码
    |我的一生|敲的总行数|敲了几天|平均每天敲几行
    |------|----|----|----|
    |C++|228|1|228|
    |lua|4629|9|514|
    
    # 这一生所处的状态
    |我的状态|天数|
    |------|----|
    |加班狂魔|2|
    |正常上班|6|
    |天天摸鱼|2|
    |开摆去咯|1|
    

    部分实现

    初始化

    lua
    local scores = {} -- 条目的熟练度,键=条目,值=分数
    local levelDays = {0, 0, 0, 0} -- 迄今为止效率,键=状态{摆烂,摸鱼,正常,高强度},值=天数
    local studyDays = {} -- 每个条目各自学习了几天
    local linesCount = {} -- 每个条目各自敲了几开始行,键=条目,值=行数
    local startDay = nil -- 从什么时候开始
    local endDay = nil -- 从什么时候结束 
    local tmpDay = nil -- 当前记录前一个记录的日子
    

    读取记录

    主要用了一个有状态的迭代器,配合泛型for循环使用。没有用到不可变状态和控制变量,但是个人习惯所以留着形参s、c。

    lua
    -- 记录迭代器,逐条返回记录
    function getRecords()
        local i = #life
        return function(s, c)
            if i > 0 then
                i = i - 1
                return life[i + 1]
            end
            return nil
        end, nil, nil
    end
    

    时间计算

    用模式匹配捕获年月日,并保存最早的时间和最晚的时间。另外计算两个时间之间的间隔sep,根据间隔大小更新scores。

    lua
    -- 根据每条记录更新 startDay 跟 endDay
    -- 计算前一条记录与后一条记录的间隔天数,统计至levelDays并进行scores每日遗忘操作
    function updateTime(date)
        local y, m, d = string.match(date, "(%d+)年(%d+)月(%d+)")
        if not startDay then
            startDay = {
                year = y,
                month = m,
                day = d
            }
        end
        tmpDay = tmpDay or startDay
        endDay = {
            year = y,
            month = m,
            day = d
        }
        -- 计算前一条记录与后一条记录的间隔天数 sep
        local sep = os.difftime(os.time(endDay), os.time(tmpDay)) // 3600 // 24
        -- 间隔大于 1 天,将sep - 1的天数归入摆烂,并进行每日遗忘操作
        if sep > 1 then
            updateLevelDays(1, sep - 1)
            dailyScores(sep)
        else
            dailyScores(1)
        end
        tmpDay = endDay
    end
    

    遗忘操作

    根据规则对scores进行减少

    lua
    -- 每日遗忘
    function dailyScores(n)
        for k, v in pairs(scores) do
            if v <= 80 then
                v = v - 1 * (n or 1)
            elseif v <= 100 then
                v = v - 2 * (n or 1)
            end
            scores[k] = v <= 0 and 0 or v
        end
    end
    

    每条记录的迭代过程

    lua
    -- 根据每条记录更新 scores levelDays linesCount
    function update(record)
        local countLines = 0 -- 当天写过的代码行数总和
        for item, v in pairs(record) do
            -- 判断是编程语言还是基础理论
            if type(v[1]) == "number" then
                local total = v[1] + (v[2] or 0)
                countLines = countLines + total
                updateLinesCount(item, total)
                updateStudyDays(item)
                local level = level(item, total)
                updateScores(item, total, level)
            elseif type(v[1]) == "string" then
                updateScores(item, #v)
                updateStudyDays(item)
            else
                error("条目格式有错", 2)
            end
        end
        -- 更新 levelDays , 保存当天状态 {摆烂,摸鱼,正常,高强度}
        updateLevelDaysByCountLines(countLines)
    end
    

    主程序

    lua
    -- 主程序
    for r in getRecords() do
        -- 根据当前记录时间更新
        updateTime(r["date"])
        -- 删除"date"减少多余的判断
        r["date"] = nil
        -- 更新 scores levelDays linesCount
        update(r)
    end
    -- 根据系统时间更新一次时间
    updateTime(os.date("%Y年%m月%d日"))
    

    排序

    table.sort 只能序列使用,所以每张表先各自拷贝成一份序列再排序

    lua
    -- 排个序 scores,linesCount
    scoresSort = {}
    for k, v in pairs(scores) do
        scoresSort[#scoresSort + 1] = {k, v}
    end
    linesCountSort = {}
    for k, v in pairs(linesCount) do
        linesCountSort[#linesCountSort + 1] = {k, v}
    end
    table.sort(scoresSort, function(a, b)
        return a[2] > b[2] -- 熟练度分数对比
    end)
    table.sort(linesCount, function(a, b)
        return a[2] > b[2] -- 总行数对比
    end)
    

    前置拼接

    如果前面还需要有固定内容,可以添加个head.md文件。如果存在则自动拼接

    lua
    io.output(io.open("output.md", "w"))
    -- 做前置拼接,一般是个人介绍的内容
    pcall(function()
        f = io.open("head.md", "r")
        if f then
            io.write(f:read("a"))
            io.write "\n"
            f:close()
        end
    end)
    

    输出表格

    输出表格按markdown格式进行输出

    lua
    -- 输出各个表格
    io.write("# IT技术熟练度\n")
    io.write(string.format("**Time : %d/%d/%d ~ %d/%d/%d**\n", startDay.year, startDay.month, startDay.day, endDay.year,
        endDay.month, endDay.day))
    io.write("|技术|学习天数|熟练度 (0~100)|程度|评级|\n")
    io.write("|------|----|----|----|----|\n")
    local cd = {"遗忘", "生疏", "了解", "熟悉", "熟练"}
    for i, v in ipairs(scoresSort) do
        local stars = ""
        for i = 1, v[2] // 20 + 1, 1 do
            stars = stars .. "⭐"
        end
        io.write(string.format("\z
        |%s|%s|%d|%s|%s|\n\z
        \z", v[1], studyDays[v[1]], v[2], cd[v[2] // 20 + 1], stars))
    end
    io.write "\n"
    

    完整代码 main.lua

    lua
    -- By 小能喵喵喵 2023年8月17日03:28:14
    -- https://www.cnblogs.com/linxiaoxu
    -- 初始化
    local scores = {} -- 条目的熟练度,键=条目,值=分数
    local levelDays = {0, 0, 0, 0} -- 迄今为止效率,键=状态{摆烂,摸鱼,正常,高强度},值=天数
    local studyDays = {} -- 每个条目总共学习了几天
    local studyDays_ = {} -- 每个条目代码敲了几天
    local linesCount = {} -- 每个条目各自敲了几开始行,键=条目,值=行数
    local startDay = nil -- 从什么时候开始
    local endDay = nil -- 从什么时候结束 
    local tmpDay = nil -- 当前记录前一个记录的日子
    
    -- 加载记录集
    dofile("life.lua")
    
    -- 记录迭代器,逐条返回记录
    function getRecords()
        local i = #life
        return function(s, c)
            if i > 0 then
                i = i - 1
                return life[i + 1]
            end
            return nil
        end, nil, nil
    end
    
    -- 根据每条记录更新 startDay 跟 endDay
    -- 计算前一条记录与后一条记录的间隔天数,统计至levelDays并进行scores每日遗忘操作
    function updateTime(date)
        local y, m, d = string.match(date, "(%d+)年(%d+)月(%d+)")
        if not startDay then
            startDay = {
                year = y,
                month = m,
                day = d
            }
        end
        tmpDay = tmpDay or startDay
        endDay = {
            year = y,
            month = m,
            day = d
        }
        -- 计算前一条记录与后一条记录的间隔天数 sep
        local sep = os.difftime(os.time(endDay), os.time(tmpDay)) // 3600 // 24
        -- 间隔大于 1 天,将sep - 1的天数归入摆烂,并进行每日遗忘操作
        if sep > 1 then
            updateLevelDays(1, sep - 1)
            dailyScores(sep)
        else
            dailyScores(1)
        end
        tmpDay = endDay
    end
    
    -- 更新 linesCount
    function updateLinesCount(item, count)
        linesCount[item] = (linesCount[item] or 0) + count
    end
    
    -- 更新 studyDays
    function updateStudyDays(item)
        studyDays[item] = (studyDays[item] or 0) + 1
    end
    
    -- 更新 studyDays_
    function updateStudyDays_(item)
        studyDays_[item] = (studyDays_[item] or 0) + 1
    end
    
    -- 每日遗忘
    function dailyScores(n)
        for k, v in pairs(scores) do
            if v <= 80 then
                v = v - 1 * (n or 1)
            elseif v <= 100 then
                v = v - 2 * (n or 1)
            end
            scores[k] = v <= 0 and 0 or v
        end
    end
    
    -- 根据 linesCount 对比得出状态{摆烂,摸鱼,正常,高强度},在updateLinesCount之后执行
    function level(item, count)
        local avg = nil
        if studyDays_[item] < 4 then -- 不足4天默认按500行计算
            avg = 500
        else
            avg = linesCount[item] // studyDays_[item]
        end
        if count >= avg * 1.25 then
            return 4
        elseif count >= avg * 0.75 then
            return 3
        elseif count >= avg * 0.25 then
            return 2
        elseif count >= avg * 0.1 then
            return 1
        else
            return 0
        end
    end
    
    -- 更新 levelDays, level = 1~4
    function updateLevelDays(level, sep)
        levelDays[level] = (levelDays[level] or 0) + (sep or 1)
    end
    
    -- 根据当天写的代码更新 levelDays
    function updateLevelDaysByCountLines(countLines)
        local totalLines = 0
        local totalDays = 0
        for _, v in pairs(linesCount) do
            totalLines = v + totalLines
        end
        for _, v in pairs(studyDays_) do
            totalDays = v + totalDays
        end
        local avgLines = totalLines // totalDays
        if totalDays < 4 then -- 总天数不足4天按500行算
            avgLines = 500
        end
        if countLines > 1.25 * avgLines then
            updateLevelDays(4)
        elseif countLines > 0.5 * avgLines then
            updateLevelDays(3)
        elseif countLines > 0.1 * avgLines then
            updateLevelDays(2)
        else
            updateLevelDays(1)
        end
    end
    
    -- 根据条目键值对加分,并更新linesCount
    function updateScores(item, count, level)
        scores[item] = scores[item] or 40
        if level then
            if level == 4 then
                scores[item] = scores[item] + 4
            elseif level == 3 then
                scores[item] = scores[item] + 3
            elseif level == 2 then
                scores[item] = scores[item] + 2
            elseif level == 1 then
                scores[item] = scores[item] + 1
            else
                -- do nothing
            end
            scores[item] = scores[item] > 100 and 100 or scores[item]
        else
            scores[item] = scores[item] + count * 2
            scores[item] = scores[item] > 100 and 100 or scores[item]
        end
    end
    
    -- 根据每条记录更新 scores levelDays linesCount
    function update(record)
        local countLines = 0 -- 当天写过的代码行数总和
        for item, v in pairs(record) do
            -- 判断是编程语言还是基础理论
            if type(v[1]) == "number" then
                local total = v[1] + (v[2] or 0)
                countLines = countLines + total
                updateLinesCount(item, total)
                updateStudyDays(item)
                updateStudyDays_(item)
                local level = level(item, total)
                updateScores(item, total, level)
            elseif type(v[1]) == "string" then
                updateScores(item, #v)
                updateStudyDays(item)
            else
                error("条目格式有错", 2)
            end
        end
        -- 更新 levelDays , 保存当天状态 {摆烂,摸鱼,正常,高强度}
        updateLevelDaysByCountLines(countLines)
    end
    
    -- 主程序
    for r in getRecords() do
        -- 根据当前记录时间更新
        updateTime(r["date"])
        -- 删除"date"减少多余的判断
        r["date"] = nil
        -- 更新 scores levelDays linesCount
        update(r)
    end
    -- 根据系统时间更新一次时间
    updateTime(os.date("%Y年%m月%d日"))
    
    -- 排个序 scores,linesCount
    scoresSort = {}
    for k, v in pairs(scores) do
        scoresSort[#scoresSort + 1] = {k, v}
    end
    linesCountSort = {}
    for k, v in pairs(linesCount) do
        linesCountSort[#linesCountSort + 1] = {k, v}
    end
    table.sort(scoresSort, function(a, b)
        return a[2] > b[2] -- 熟练度分数对比
    end)
    table.sort(linesCount, function(a, b)
        return a[2] > b[2] -- 总行数对比
    end)
    
    -- 输出结果
    io.output(io.open("output.md", "w"))
    -- 做前置拼接,一般是个人介绍的内容
    pcall(function()
        f = io.open("head.md", "r")
        if f then
            io.write(f:read("a"))
            io.write "\n"
            f:close()
        end
    end)
    -- 输出各个表格
    io.write("# IT技术熟练度\n")
    io.write(string.format("**Time : %d/%d/%d ~ %d/%d/%d**\n", startDay.year, startDay.month, startDay.day, endDay.year,
        endDay.month, endDay.day))
    io.write("|技术|学习天数|熟练度 (0~100)|程度|评级|\n")
    io.write("|------|----|----|----|----|\n")
    local cd = {"遗忘", "生疏", "了解", "熟悉", "熟练"}
    for i, v in ipairs(scoresSort) do
        local stars = ""
        for i = 1, v[2] // 20 + 1, 1 do
            stars = stars .. "⭐"
        end
        io.write(string.format("\z
        |%s|%s|%d|%s|%s|\n\z
        \z", v[1], studyDays[v[1]], v[2], cd[v[2] // 20 + 1], stars))
    end
    io.write "\n"
    io.write("# 这一生写过的代码\n")
    io.write("|我的一生|敲的总行数|敲了几天|平均每天几行\n")
    io.write("|------|----|----|----|\n")
    for i, v in ipairs(linesCountSort) do
        io.write(string.format("|%s|%d|%d|%d|\n", v[1], v[2], studyDays_[v[1]], v[2] // studyDays_[v[1]]))
    end
    io.write "\n"
    io.write("# 这一生所处的状态\n")
    io.write("|我的状态|天数|\n")
    io.write("|------|----|\n")
    io.write(string.format("|加班狂魔|%d|\n", levelDays[4]))
    io.write(string.format("|正常上班|%d|\n", levelDays[3]))
    io.write(string.format("|天天摸鱼|%d|\n", levelDays[2]))
    io.write(string.format("|开摆去咯|%d|\n", levelDays[1]))
    io.write("\n")
    io.write("# 这一生活动的统计\n")
    io.write("使用插件 `VS Code Counter` 进行每日统计,仅统计有效行,不含空行\n")
    io.write("```lua\n")
    f = io.open("life.lua")
    io.write(f:read("a"))
    f:close()
    io.write("```\n")
    io.output():close()
    

    如何获取行数


    __EOF__

  • 本文作者: 小能正在往前冲
  • 本文链接: https://www.cnblogs.com/linxiaoxu/p/17637572.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    深度学习实战(1):花的分类任务
    web前端面试题附答案002-说一下react组件间的数据传递
    “开放SDK,创造无限可能”,「四维轻云」又有新功能上线!攻略来了
    Google codelab WebGPU入门教程源码<3> - 绘制网格(源码)
    SREWorks v1.3 版本发布 | 插件机制发布
    口袋参谋:淘宝卖家必备的市场调查分析工具!
    JVM虚拟机理解
    cdh3.6.2集成zeppelin0.10.1及简单使用
    怎样把1.ts-10.ts的文件拼接成一个MP4文件
    音视频封装demo:使用libmp4v2将h264视频数据和aac语音数据封装(mux)成mp4文件
  • 原文地址:https://www.cnblogs.com/linxiaoxu/p/17637572.html