Jelajahi Sumber

feat(report): add teacher weekly stats script and notes

Made-with: Cursor
yemeishu 2 bulan lalu
induk
melakukan
333fe0ec47
2 mengubah file dengan 450 tambahan dan 0 penghapusan
  1. 247 0
      scripts/report_teacher_weekly_stats.php
  2. 203 0
      teacher-weekly-stats.md

+ 247 - 0
scripts/report_teacher_weekly_stats.php

@@ -0,0 +1,247 @@
+<?php
+
+/**
+ * 近 7 天老师组卷 + 学情分析套数(exam_analysis_results 按 paper_id 去重,一套卷计 1)
+ * 用法:
+ *   php scripts/report_teacher_weekly_stats.php
+ *   php scripts/report_teacher_weekly_stats.php > storage/app/reports/teacher-weekly-stats-$(date +%Y-%m-%d)_$(date +%H%M%S).md
+ */
+
+require __DIR__ . '/../vendor/autoload.php';
+
+$app = require_once __DIR__ . '/../bootstrap/app.php';
+$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
+
+$endCurrent = now();
+$startCurrent = now()->subDays(7);
+$startPrev = now()->subDays(14);
+$endPrev = $startCurrent;
+
+$db = \Illuminate\Support\Facades\DB::class;
+
+$sumPapers = static function ($from, $toExclusive) use ($db) {
+    $q = $db::table('papers')
+        ->whereNotNull('teacher_id')
+        ->where('teacher_id', '!=', '')
+        ->where('created_at', '>=', $from);
+    if ($toExclusive !== null) {
+        $q->where('created_at', '<', $toExclusive);
+    }
+
+    return (int) $q->count();
+};
+
+/** 学情分析:以卷子为单位,同一 paper_id 在区间内多条记录只计 1 */
+$countDistinctAnalysisPapers = static function ($from, $toExclusive) use ($db) {
+    $q = $db::table('exam_analysis_results as ear')
+        ->join('papers as p', 'p.paper_id', '=', 'ear.paper_id')
+        ->whereNotNull('p.teacher_id')
+        ->where('p.teacher_id', '!=', '')
+        ->where('ear.created_at', '>=', $from);
+    if ($toExclusive !== null) {
+        $q->where('ear.created_at', '<', $toExclusive);
+    }
+
+    return (int) $q->distinct()->count('ear.paper_id');
+};
+
+$countActiveTeachers = static function ($from, $toExclusive) use ($db) {
+    $q = $db::table('papers')
+        ->whereNotNull('teacher_id')
+        ->where('teacher_id', '!=', '')
+        ->where('created_at', '>=', $from);
+    if ($toExclusive !== null) {
+        $q->where('created_at', '<', $toExclusive);
+    }
+
+    return (int) $q->distinct()->count('teacher_id');
+};
+
+$totalPapersCur = $sumPapers($startCurrent, null);
+$totalPapersPrev = $sumPapers($startPrev, $endPrev);
+$totalAnalysisCur = $countDistinctAnalysisPapers($startCurrent, null);
+$totalAnalysisPrev = $countDistinctAnalysisPapers($startPrev, $endPrev);
+$teachersCur = $countActiveTeachers($startCurrent, null);
+$teachersPrev = $countActiveTeachers($startPrev, $endPrev);
+
+$wowLine = static function (int $cur, int $prev): string {
+    $delta = $cur - $prev;
+    if ($prev === 0) {
+        if ($cur === 0) {
+            return '0';
+        }
+
+        return sprintf('+%d(上周期0)', $delta);
+    }
+    $pct = round(($delta / $prev) * 100, 2);
+    $sign = $delta >= 0 ? '+' : '';
+    $dir = match (true) {
+        $delta > 0 => '↑',
+        $delta < 0 => '↓',
+        default => '→',
+    };
+
+    return sprintf('%s%d(%s%.2f%%)%s', $sign, $delta, $sign, $pct, $dir);
+};
+
+/** 总量表环比列:仅增长标绿 */
+$wowLineHtml = static function (int $cur, int $prev) use ($wowLine): string {
+    $plain = $wowLine($cur, $prev);
+    if ($cur > $prev) {
+        return '<span style="color:#16a34a;font-weight:600;">'.$plain.'</span>';
+    }
+
+    return $plain;
+};
+
+/** 明细环比列:仅增长标绿 */
+$compareCellHtml = static function (int $cur, int $prev): string {
+    $d = $cur - $prev;
+    if ($d === 0) {
+        return '0';
+    }
+    if ($prev === 0) {
+        if ($cur === 0) {
+            return '0';
+        }
+
+        return '<span style="color:#16a34a;font-weight:600;">+'.$d.'(上0)</span>';
+    }
+    $pct = round(($d / $prev) * 100, 1);
+    $sign = $d > 0 ? '+' : '';
+    $text = sprintf('%s%d(%s%.1f%%)', $sign, $d, $sign, $pct);
+    if ($d > 0) {
+        return '<span style="color:#16a34a;font-weight:600;">'.$text.'</span>';
+    }
+
+    return $text;
+};
+
+$byTeacher = \Illuminate\Support\Facades\DB::table('papers')
+    ->whereNotNull('teacher_id')
+    ->where('teacher_id', '!=', '')
+    ->where('created_at', '>=', $startCurrent)
+    ->selectRaw('teacher_id, COUNT(*) as paper_count')
+    ->groupBy('teacher_id')
+    ->get();
+
+// 近 7 天产生学情分析的试卷套数:按 ear.paper_id 去重后归到 papers.teacher_id
+$analysisByTeacher = \Illuminate\Support\Facades\DB::table('exam_analysis_results as ear')
+    ->join('papers as p', 'p.paper_id', '=', 'ear.paper_id')
+    ->whereNotNull('p.teacher_id')
+    ->where('p.teacher_id', '!=', '')
+    ->where('ear.created_at', '>=', $startCurrent)
+    ->selectRaw('p.teacher_id, COUNT(DISTINCT ear.paper_id) AS paper_set_count')
+    ->groupBy('p.teacher_id')
+    ->get();
+
+$analysisMap = [];
+foreach ($analysisByTeacher as $r) {
+    $analysisMap[(string) $r->teacher_id] = (int) $r->paper_set_count;
+}
+
+$paperMap = [];
+foreach ($byTeacher as $r) {
+    $paperMap[(string) $r->teacher_id] = (int) $r->paper_count;
+}
+
+$byTeacherPrev = $db::table('papers')
+    ->whereNotNull('teacher_id')
+    ->where('teacher_id', '!=', '')
+    ->where('created_at', '>=', $startPrev)
+    ->where('created_at', '<', $endPrev)
+    ->selectRaw('teacher_id, COUNT(*) as paper_count')
+    ->groupBy('teacher_id')
+    ->get();
+
+$analysisByTeacherPrev = $db::table('exam_analysis_results as ear')
+    ->join('papers as p', 'p.paper_id', '=', 'ear.paper_id')
+    ->whereNotNull('p.teacher_id')
+    ->where('p.teacher_id', '!=', '')
+    ->where('ear.created_at', '>=', $startPrev)
+    ->where('ear.created_at', '<', $endPrev)
+    ->selectRaw('p.teacher_id, COUNT(DISTINCT ear.paper_id) AS paper_set_count')
+    ->groupBy('p.teacher_id')
+    ->get();
+
+$paperMapPrev = [];
+foreach ($byTeacherPrev as $r) {
+    $paperMapPrev[(string) $r->teacher_id] = (int) $r->paper_count;
+}
+
+$analysisMapPrev = [];
+foreach ($analysisByTeacherPrev as $r) {
+    $analysisMapPrev[(string) $r->teacher_id] = (int) $r->paper_set_count;
+}
+
+$names = \Illuminate\Support\Facades\DB::table('teachers')->pluck('name', 'teacher_id');
+$nameStrMap = [];
+foreach ($names as $tid => $nm) {
+    $nameStrMap[(string) $tid] = $nm;
+}
+
+$rows = [];
+foreach ($paperMap as $tid => $paperCount) {
+    $rows[] = [
+        'teacher_id' => $tid,
+        'name' => (string) ($nameStrMap[$tid] ?? $tid),
+        'papers' => $paperCount,
+        'analysis_sets' => (int) ($analysisMap[$tid] ?? 0),
+        'papers_prev' => (int) ($paperMapPrev[$tid] ?? 0),
+        'analysis_sets_prev' => (int) ($analysisMapPrev[$tid] ?? 0),
+    ];
+}
+
+usort($rows, static fn ($a, $b) => $b['papers'] <=> $a['papers']);
+
+$windowCur = sprintf(
+    '%s ~ %s',
+    $startCurrent->format('Y-m-d H:i:s'),
+    $endCurrent->format('Y-m-d H:i:s')
+);
+$windowPrev = sprintf(
+    '%s ~ %s',
+    $startPrev->format('Y-m-d H:i:s'),
+    $endPrev->format('Y-m-d H:i:s')
+);
+
+$generatedAt = $endCurrent->format('Y-m-d H:i:s');
+$tz = (string) config('app.timezone', 'UTC');
+
+echo "## 老师组卷与学情分析(近7天)\n\n";
+echo "> 生成 {$generatedAt} · {$tz} · 本 {$windowCur} · 上 {$windowPrev}\n\n";
+
+echo "### 总量\n\n";
+echo "| 指标 | 本周期 | 上周期 | 环比 |\n";
+echo "| --- | ---: | ---: | --- |\n";
+echo sprintf("| 组卷总套数 | %d | %d | %s |\n", $totalPapersCur, $totalPapersPrev, $wowLineHtml($totalPapersCur, $totalPapersPrev));
+echo sprintf("| 学情分析套数(卷去重) | %d | %d | %s |\n", $totalAnalysisCur, $totalAnalysisPrev, $wowLineHtml($totalAnalysisCur, $totalAnalysisPrev));
+echo sprintf("| 有组卷老师数 | %d | %d | %s |\n", $teachersCur, $teachersPrev, $wowLineHtml($teachersCur, $teachersPrev));
+
+echo "\n### 按老师\n\n";
+
+echo "| 排名 | 老师 | teacher_id | 组卷·本 | 组卷·上 | 组卷·环比 | 学情·本 | 学情·上 | 学情·环比 |\n";
+echo "| ---: | --- | --- | ---: | ---: | --- | ---: | ---: | --- |\n";
+
+$i = 1;
+foreach ($rows as $r) {
+    $nm = str_replace(['|', "\n", '<', '>'], ['/', ' ', '', ''], $r['name']);
+    $pc = $r['papers'];
+    $pp = $r['papers_prev'];
+    $ac = $r['analysis_sets'];
+    $ap = $r['analysis_sets_prev'];
+    echo sprintf(
+        "| %d | %s | %s | %d | %d | %s | %d | %d | %s |\n",
+        $i++,
+        $nm,
+        $r['teacher_id'],
+        $pc,
+        $pp,
+        $compareCellHtml($pc, $pp),
+        $ac,
+        $ap,
+        $compareCellHtml($ac, $ap)
+    );
+}
+
+echo sprintf("\n本周期有组卷 **%d** 人。\n", count($rows));

+ 203 - 0
teacher-weekly-stats.md

@@ -0,0 +1,203 @@
+## 老师组卷与学情分析统计(近 7 天)
+
+### 总量与环比
+
+- **本周期**:2026-03-21 16:30:26 ~ 2026-03-28 16:30:26;**上周期(对比用)**:2026-03-14 16:30:26 ~ 2026-03-21 16:30:26。均以数据库 `created_at` 为准;上周期为左闭右开区间,右端点为本周期的起始时刻。
+
+| 指标 | 本周期(近7天) | 上周期(再往前7天) | 环比(增减 / 涨跌幅 / 方向) |
+| --- | ---: | ---: | --- |
+| 组卷总套数(`papers`,老师非空) | 2114 | 2201 | **-87**(**-3.95%**)下跌 |
+| 学情分析套数(`exam_analysis_results`,去重 `paper_id`) | 1438 | 1553 | **-115**(**-7.41%**)下跌 |
+| 有组卷行为的老师数(去重 `teacher_id`) | 182 | 183 | **-1**(**-0.55%**)下跌 |
+
+### 口径说明
+
+- **组卷套数(下表按老师)**:本周期内 `papers` 创建且 `teacher_id` 非空。
+- **学情分析套数**:本周期内 `exam_analysis_results` 有记录且能关联到 `papers`;**同一 `paper_id` 无论几条分析记录只计 1 套**;按 `papers.teacher_id` 归到老师。
+
+| 排名 | 老师 | teacher_id | 组卷套数 | 学情分析套数 |
+| ---: | --- | --- | ---: | ---: |
+| 1 | 龚 | 433 | 262 | 173 |
+| 2 | 孙金娣 | 175 | 98 | 79 |
+| 3 | 陈老师 | 59 | 88 | 70 |
+| 4 | 陈俊良 | 86 | 84 | 85 |
+| 5 | 张老师 | 415 | 69 | 42 |
+| 6 | 李堃 | 450 | 52 | 31 |
+| 7 | 高梦泽 | 207 | 51 | 48 |
+| 8 | 好老师 | 114 | 48 | 9 |
+| 9 | 陈飞 | 171 | 44 | 40 |
+| 10 | 胡老师 | 51 | 42 | 36 |
+| 11 | 王老师 | 462 | 42 | 35 |
+| 12 | 刘老师 | 240 | 39 | 27 |
+| 13 | 喻艳 | 321 | 37 | 32 |
+| 14 | 王老师 | 338 | 36 | 29 |
+| 15 | 黄梦圆 | 144 | 35 | 31 |
+| 16 | 杜 | 147 | 33 | 22 |
+| 17 | 张老师 | 285 | 31 | 28 |
+| 18 | 汪静粼 | 106 | 29 | 20 |
+| 19 | 雷航 | 199 | 27 | 14 |
+| 20 | 姚哥 | 45 | 26 | 0 |
+| 21 | 程方圆 | 379 | 24 | 15 |
+| 22 | 王灵珠 | 223 | 24 | 10 |
+| 23 | 戴老师 | 584 | 22 | 20 |
+| 24 | 葛凡 | 378 | 22 | 18 |
+| 25 | 刘老师 | 170 | 22 | 15 |
+| 26 | 孙晓云 | 200 | 21 | 16 |
+| 27 | 好老师 | 535 | 20 | 15 |
+| 28 | 王老师 | 538 | 20 | 10 |
+| 29 | 木子老师 | 256 | 20 | 17 |
+| 30 | 赵杰东 | 192 | 20 | 19 |
+| 31 | 叶仁泳 | 587 | 19 | 8 |
+| 32 | 冯盼盼 | 104 | 18 | 16 |
+| 33 | 李老师 | 500 | 17 | 10 |
+| 34 | 郭老师 | 181 | 16 | 14 |
+| 35 | 黄老师 | 325 | 15 | 7 |
+| 36 | 育才 | 138 | 15 | 16 |
+| 37 | 王老师 | 418 | 13 | 11 |
+| 38 | 李老师 | 408 | 13 | 14 |
+| 39 | 韩老师 | 413 | 12 | 9 |
+| 40 | 霍老师 | 393 | 12 | 7 |
+| 41 | 封 | 167 | 12 | 10 |
+| 42 | 赵老师 | 60 | 11 | 9 |
+| 43 | 闫老师 | 242 | 11 | 8 |
+| 44 | 徐洪艳 | 419 | 11 | 2 |
+| 45 | 邹老师 | 544 | 11 | 10 |
+| 46 | 陈新杰 | 394 | 11 | 2 |
+| 47 | 蒋老师 | 526 | 10 | 1 |
+| 48 | 李老师 | 168 | 10 | 8 |
+| 49 | 郗老师 | 70 | 10 | 2 |
+| 50 | 宋丹烁 | 310 | 10 | 10 |
+| 51 | 陈老师 | 509 | 9 | 5 |
+| 52 | 松柏 | 232 | 9 | 5 |
+| 53 | 刘老师 | 108 | 9 | 4 |
+| 54 | 戴睿 | 140 | 8 | 4 |
+| 55 | 赵老师 | 132 | 8 | 6 |
+| 56 | 姚老师 | 105 | 8 | 1 |
+| 57 | 李文丹青 | 197 | 8 | 8 |
+| 58 | 武金凤 | 417 | 8 | 4 |
+| 59 | 尹梦倩 | 366 | 7 | 4 |
+| 60 | 蔡老师 | 283 | 7 | 5 |
+| 61 | 郭老师 | 374 | 7 | 5 |
+| 62 | 刘佳涵 | 529 | 7 | 8 |
+| 63 | 徐琳 | 507 | 7 | 6 |
+| 64 | 李老师 | 29 | 7 | 0 |
+| 65 | 赵老师 | 186 | 7 | 0 |
+| 66 | 张 | 64 | 7 | 0 |
+| 67 | 高宝华 | 117 | 7 | 2 |
+| 68 | 郑老师 | 368 | 7 | 10 |
+| 69 | 王旭耀 | 380 | 7 | 1 |
+| 70 | 赵老师 | 356 | 6 | 7 |
+| 71 | 小魏 | 522 | 6 | 2 |
+| 72 | 张老师 | 422 | 6 | 3 |
+| 73 | 刘崇 | 599 | 6 | 2 |
+| 74 | 徐老师 | 425 | 6 | 0 |
+| 75 | 周 | 219 | 6 | 5 |
+| 76 | 张习习 | 296 | 6 | 8 |
+| 77 | 黄老师 | 608 | 6 | 4 |
+| 78 | 刘老师 | 329 | 6 | 2 |
+| 79 | 臧老师 | 178 | 6 | 1 |
+| 80 | 何老师 | 110 | 5 | 7 |
+| 81 | 田老师 | 203 | 5 | 2 |
+| 82 | 文学 | 533 | 5 | 2 |
+| 83 | 刘老师 | 326 | 5 | 3 |
+| 84 | 贾老师 | 504 | 5 | 4 |
+| 85 | 张老师 | 595 | 5 | 2 |
+| 86 | 杜老师 | 531 | 5 | 4 |
+| 87 | 肖老师 | 385 | 5 | 5 |
+| 88 | 王老师 | 205 | 5 | 2 |
+| 89 | 孙老师 | 330 | 5 | 4 |
+| 90 | 申老师 | 331 | 5 | 2 |
+| 91 | 朱老师 | 530 | 5 | 2 |
+| 92 | 教师1 | 611 | 5 | 1 |
+| 93 | 凡书书 | 597 | 4 | 3 |
+| 94 | 黄海霞 | 506 | 4 | 2 |
+| 95 | 吉老师 18098402130 | 442 | 4 | 5 |
+| 96 | 娜娜老师 | 453 | 4 | 0 |
+| 97 | 刘老师 | 351 | 4 | 3 |
+| 98 | 刘老师 | 465 | 4 | 3 |
+| 99 | 唐诗 | 398 | 4 | 4 |
+| 100 | 刘 | 92 | 4 | 2 |
+| 101 | 小赵老师 | 159 | 4 | 5 |
+| 102 | 孟老师 | 101 | 4 | 3 |
+| 103 | 史老师 | 582 | 4 | 1 |
+| 104 | 好老师 | 75 | 4 | 2 |
+| 105 | 黄老师 | 282 | 4 | 0 |
+| 106 | 小知老师 | 324 | 4 | 4 |
+| 107 | 李老师 | 66 | 4 | 0 |
+| 108 | 许静 | 190 | 4 | 1 |
+| 109 | 曹琳 | 230 | 3 | 3 |
+| 110 | 仲老师 | 583 | 3 | 1 |
+| 111 | 聂老师 | 107 | 3 | 2 |
+| 112 | 小张 | 248 | 3 | 1 |
+| 113 | 王老师 | 423 | 3 | 4 |
+| 114 | 花蕾老师 | 556 | 3 | 0 |
+| 115 | 秦老师 | 99 | 3 | 3 |
+| 116 | 马丰丰 | 176 | 3 | 2 |
+| 117 | 赵老师 | 183 | 3 | 2 |
+| 118 | 陶荣 | 604 | 3 | 0 |
+| 119 | 戴 | 58 | 3 | 2 |
+| 120 | 张老师 | 610 | 3 | 1 |
+| 121 | 郑灿林 | 524 | 3 | 0 |
+| 122 | 张哲僮校长 | 281 | 3 | 1 |
+| 123 | 张老师 | 612 | 3 | 1 |
+| 124 | 张老师 | 613 | 3 | 1 |
+| 125 | 璇 | 96 | 3 | 1 |
+| 126 | 木子老师 | 502 | 3 | 5 |
+| 127 | 姚老师 | 40 | 3 | 0 |
+| 128 | 李老师 | 228 | 2 | 0 |
+| 129 | 张琳娜 | 598 | 2 | 0 |
+| 130 | 陈曦 | 166 | 2 | 0 |
+| 131 | 袁海超 | 328 | 2 | 1 |
+| 132 | 刘老师 | 340 | 2 | 1 |
+| 133 | 小露老师 | 427 | 2 | 0 |
+| 134 | 乔老师 | 91 | 2 | 0 |
+| 135 | 王武汉 | 518 | 2 | 1 |
+| 136 | 数学征征老师 | 145 | 2 | 2 |
+| 137 | 安敏 | 346 | 2 | 0 |
+| 138 | 李老师 | 347 | 2 | 2 |
+| 139 | 彭老师 | 600 | 2 | 1 |
+| 140 | 林老师 | 34 | 2 | 2 |
+| 141 | 刘梦慧 | 603 | 2 | 1 |
+| 142 | 徐老师 | 421 | 2 | 1 |
+| 143 | 丁老师 | 605 | 2 | 1 |
+| 144 | 吕老师 | 102 | 2 | 0 |
+| 145 | 高静 | 198 | 2 | 2 |
+| 146 | 舒老师 | 52 | 2 | 1 |
+| 147 | 侯老师 | 528 | 2 | 1 |
+| 148 | 何老师 | 210 | 2 | 1 |
+| 149 | 龚老师 | 490 | 2 | 1 |
+| 150 | 高学英 | 115 | 2 | 1 |
+| 151 | 冯 | 113 | 2 | 0 |
+| 152 | 李粉蝶 | 399 | 1 | 0 |
+| 153 | 彭老师 | 348 | 1 | 0 |
+| 154 | 颜老师 | 457 | 1 | 0 |
+| 155 | 王老师 | 227 | 1 | 1 |
+| 156 | 张峰 | 237 | 1 | 0 |
+| 157 | 陈老师 | 279 | 1 | 1 |
+| 158 | 谭树 | 267 | 1 | 0 |
+| 159 | 林老师 | 480 | 1 | 1 |
+| 160 | 于老师 | 601 | 1 | 0 |
+| 161 | 张泽锴 | 602 | 1 | 0 |
+| 162 | 测试 | 53 | 1 | 0 |
+| 163 | 周老师 | 65 | 1 | 0 |
+| 164 | 刘静 | 606 | 1 | 1 |
+| 165 | 温碧莹 | 607 | 1 | 1 |
+| 166 | 王老师 | 609 | 1 | 0 |
+| 167 | 李颖 | 254 | 1 | 0 |
+| 168 | 高乾烨 | 136 | 1 | 0 |
+| 169 | 单老师 | 187 | 1 | 1 |
+| 170 | 李老师 | 446 | 1 | 0 |
+| 171 | 高老师 | 349 | 1 | 2 |
+| 172 | 简老师 | 257 | 1 | 1 |
+| 173 | 张老师 | 243 | 1 | 0 |
+| 174 | 褚老师 | 614 | 1 | 0 |
+| 175 | 彭老师 | 615 | 1 | 0 |
+| 176 | 小熊 | 353 | 1 | 3 |
+| 177 | 刘大立 | 289 | 1 | 1 |
+| 178 | 杜慧贤 | 251 | 1 | 1 |
+| 179 | 党艳芳 | 250 | 1 | 1 |
+| 180 | 周老师 | 151 | 1 | 0 |
+| 181 | 马慧香 | 184 | 1 | 1 |
+| 182 | 伏老师 | 596 | 1 | 0 |
+
+共 **182** 位老师在近 7 天有组卷记录。