chengjie пре 1 недеља
родитељ
комит
d3f661720e

+ 1 - 1
.nvmrc

@@ -1 +1 @@
1
-24.1.0
1
+24.18.0

+ 3 - 3
.vscode/launch.json

@@ -9,14 +9,14 @@
9 9
                 "<node_internals>/**"
10 10
             ],
11 11
             "program": "${workspaceFolder}/src/app.js",
12
-            "runtimeVersion": "24.1.0"
12
+            "runtimeExecutable": "/Users/chengjie/.nvm/versions/node/v24.18.0/bin/node"
13 13
         },
14 14
         {
15 15
             "type": "node-terminal",
16 16
             "name": "Run Script: start",
17 17
             "request": "launch",
18
-            "command": "npm run start",
18
+            "command": "source ~/.nvm/nvm.sh && nvm use 24.18.0 && npm run start",
19 19
             "cwd": "${workspaceFolder}"
20 20
         }
21 21
     ]
22
-}
22
+}

+ 11 - 4
.vscode/settings.json

@@ -1,6 +1,6 @@
1 1
 {
2 2
     "code-runner.executorMap": {
3
-        "javascript": "/Users/chengjie/.nvm/versions/node/v24.1.0/bin/node $fileName"
3
+        "javascript": "/Users/chengjie/.nvm/versions/node/v24.18.0/bin/node $fileName"
4 4
     },
5 5
     "files.autoSave": "afterDelay",
6 6
     "files.autoSaveDelay": 1000,
@@ -39,12 +39,19 @@
39 39
         "statusBar.background": "#1857a4",
40 40
         "statusBar.foreground": "#e7e7e7",
41 41
         "statusBarItem.hoverBackground": "#1f6fd0",
42
-        "statusBarItem.remoteBackground": "#1857a4",
42
+        "statusBarItem.remoteBackground": "#530c2c",
43 43
         "statusBarItem.remoteForeground": "#e7e7e7",
44 44
         "titleBar.activeBackground": "#1857a4",
45 45
         "titleBar.activeForeground": "#e7e7e7",
46 46
         "titleBar.inactiveBackground": "#1857a499",
47
-        "titleBar.inactiveForeground": "#e7e7e799"
47
+        "titleBar.inactiveForeground": "#e7e7e799",
48
+        "activityBarTop.activeBackground": "#1f6fd0",
49
+        "activityBarTop.background": "#1f6fd0",
50
+        "activityBarTop.foreground": "#e7e7e7",
51
+        "activityBarTop.inactiveForeground": "#e7e7e799",
52
+        "commandCenter.foreground": "#e7e7e7",
53
+        "statusBar.debuggingBackground": "#1857a4",
54
+        "statusBar.debuggingForeground": "#e7e7e7"
48 55
     },
49 56
     "peacock.color": "#1857a4"
50
-}
57
+}

+ 1 - 1
.vscode/tasks.json

@@ -4,7 +4,7 @@
4 4
         {
5 5
             "label": "Kill Running Process and Run Code",
6 6
             "type": "shell",
7
-            "command": "pkill -f 'node ${file}' || true && node ${file}",
7
+            "command": "pkill -f 'node ${file}' || true && /Users/chengjie/.nvm/versions/node/v24.18.0/bin/node ${file}",
8 8
             "group": {
9 9
                 "kind": "build",
10 10
                 "isDefault": true

Разлика између датотеке није приказан због своје велике величине
+ 2255 - 1385
package-lock.json


+ 15 - 12
package.json

@@ -8,19 +8,19 @@
8 8
   },
9 9
   "scripts": {
10 10
     "predev": "node scripts/check-node-version.js",
11
-    "dev": "./scripts/use-node-version.sh cross-env NODE_ENV=development node src/app.js",
11
+    "dev": "cross-env NODE_ENV=development node src/app.js",
12 12
     "prestart": "node scripts/check-node-version.js",
13
-    "start": "./scripts/use-node-version.sh cross-env NODE_ENV=production node src/app.js",
13
+    "start": "cross-env NODE_ENV=production node src/app.js",
14 14
     "prebuild": "node scripts/check-node-version.js",
15
-    "build": "./scripts/use-node-version.sh cross-env NODE_ENV=production node src/build.js",
16
-    "error-log:pm2": "./scripts/use-node-version.sh node scripts/collect-pm2-error-logs.js",
17
-    "error-log:poll": "./scripts/use-node-version.sh node scripts/poll-error-logs.js",
18
-    "test": "./scripts/use-node-version.sh node",
19
-    "node": "./scripts/use-node-version.sh node",
20
-    "check-version": "./scripts/use-node-version.sh node -v"
15
+    "build": "cross-env NODE_ENV=production node src/build.js",
16
+    "error-log:pm2": "node scripts/collect-pm2-error-logs.js",
17
+    "error-log:poll": "node scripts/poll-error-logs.js",
18
+    "test": "node --check src/app.js",
19
+    "node": "node",
20
+    "check-version": "node scripts/check-node-version.js"
21 21
   },
22 22
   "engines": {
23
-    "node": "24.1.0"
23
+    "node": ">=24.1.0 <27"
24 24
   },
25 25
   "keywords": [],
26 26
   "author": "",
@@ -31,7 +31,6 @@
31 31
     "@koa/router": "^13.1.0",
32 32
     "axios": "^1.9.0",
33 33
     "cheerio": "^1.1.2",
34
-    "child_process": "^1.0.2",
35 34
     "cos-nodejs-sdk-v5": "^2.16.0-beta.3",
36 35
     "gm": "^1.25.1",
37 36
     "iconv-lite": "^0.7.0",
@@ -48,10 +47,14 @@
48 47
     "mysql2": "^3.14.1",
49 48
     "node-schedule": "^2.1.1",
50 49
     "pdfkit": "^0.17.1",
51
-    "request-promise": "^4.2.6",
52
-    "tencentcloud-sdk-nodejs-ocr": "^4.1.70",
50
+    "tencentcloud-sdk-nodejs-ocr": "^4.1.268",
53 51
     "uuid": "^13.0.0"
54 52
   },
53
+  "overrides": {
54
+    "tencentcloud-sdk-nodejs-common": {
55
+      "uuid": "^11.1.1"
56
+    }
57
+  },
55 58
   "devDependencies": {
56 59
     "cross-env": "^7.0.3"
57 60
   }

+ 67 - 43
scripts/check-node-version.js

@@ -1,19 +1,21 @@
1 1
 #!/usr/bin/env node
2 2
 
3
-import fs from 'fs';
4
-import path from 'path';
5
-import { execSync } from 'child_process';
6
-import { fileURLToPath } from 'url';
3
+import fs from 'node:fs';
4
+import path from 'node:path';
5
+import { fileURLToPath } from 'node:url';
7 6
 
8 7
 const __filename = fileURLToPath(import.meta.url);
9 8
 const __dirname = path.dirname(__filename);
9
+const projectRoot = path.join(__dirname, '..');
10 10
 
11
-// 读取 .nvmrc 文件获取所需的 Node.js 版本
12
-let requiredVersion;
11
+let preferredVersion;
12
+let supportedRange;
13 13
 try {
14
-  requiredVersion = fs.readFileSync(path.join(__dirname, '..', '.nvmrc'), 'utf8').trim();
14
+  preferredVersion = fs.readFileSync(path.join(projectRoot, '.nvmrc'), 'utf8').trim();
15
+  const packageJson = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
16
+  supportedRange = packageJson.engines?.node || preferredVersion;
15 17
 } catch (error) {
16
-  console.error('错误: 无法读取 .nvmrc 文件');
18
+  console.error('错误: 无法读取 Node.js 版本配置');
17 19
   process.exit(1);
18 20
 }
19 21
 
@@ -21,55 +23,77 @@ const currentVersion = process.version.slice(1); // 移除版本号前的 'v'
21 23
 
22 24
 console.log(`检查 Node.js 版本...`);
23 25
 console.log(`当前版本: ${currentVersion}`);
24
-console.log(`所需版本: ${requiredVersion}`);
26
+console.log(`推荐版本: ${preferredVersion}`);
27
+console.log(`支持范围: ${supportedRange}`);
25 28
 
26
-// 解析版本号的所有部分
27 29
 const parseVersion = (version) => {
28
-  const parts = version.split('.').map(Number);
30
+  const match = String(version).trim().replace(/^v/, '').match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
31
+  if (!match) {
32
+    throw new Error(`Invalid version: ${version}`);
33
+  }
34
+
29 35
   return {
30
-    major: parts[0],
31
-    minor: parts[1] || 0,
32
-    patch: parts[2] || 0
36
+    major: Number(match[1]),
37
+    minor: Number(match[2] || 0),
38
+    patch: Number(match[3] || 0)
33 39
   };
34 40
 };
35 41
 
36 42
 const current = parseVersion(currentVersion);
37
-const required = parseVersion(requiredVersion);
38 43
 
39
-// 检查版本是否匹配
40
-const isVersionMatch = 
41
-  current.major === required.major &&
42
-  current.minor >= required.minor;
44
+const compareVersions = (left, right) => {
45
+  for (const key of ['major', 'minor', 'patch']) {
46
+    if (left[key] > right[key]) return 1;
47
+    if (left[key] < right[key]) return -1;
48
+  }
49
+  return 0;
50
+};
51
+
52
+const satisfiesComparator = (version, comparator) => {
53
+  const match = comparator.match(/^(>=|>|<=|<|=)?(.+)$/);
54
+  const operator = match[1] || '=';
55
+  const required = parseVersion(match[2]);
56
+  const comparison = compareVersions(version, required);
57
+
58
+  switch (operator) {
59
+    case '>=':
60
+      return comparison >= 0;
61
+    case '>':
62
+      return comparison > 0;
63
+    case '<=':
64
+      return comparison <= 0;
65
+    case '<':
66
+      return comparison < 0;
67
+    case '=':
68
+      return comparison === 0;
69
+    default:
70
+      return false;
71
+  }
72
+};
43 73
 
44
-if (!isVersionMatch) {
74
+const satisfiesRange = (version, range) => {
75
+  return range
76
+    .split('||')
77
+    .some((rangePart) =>
78
+      rangePart
79
+        .trim()
80
+        .split(/\s+/)
81
+        .filter(Boolean)
82
+        .every((comparator) => satisfiesComparator(version, comparator))
83
+    );
84
+};
85
+
86
+if (!satisfiesRange(current, supportedRange)) {
45 87
   console.error(`\n错误: Node.js 版本不匹配!`);
46
-  console.error(`本项目需要 Node.js ${requiredVersion}`);
88
+  console.error(`本项目支持 Node.js ${supportedRange}`);
47 89
   console.error(`当前使用的是 Node.js ${currentVersion}`);
48 90
   
49 91
   console.error(`\n请尝试以下方法之一:`);
50
-  console.error(`1. 使用项目提供的版本切换脚本:`);
51
-  console.error(`   ./scripts/use-node-version.sh node -v`);
52
-  console.error(`2. 如果已安装 nvm: 运行 'nvm use ${requiredVersion}'`);
53
-  console.error(`3. 安装所需版本: 'nvm install ${requiredVersion}'`);
54
-  console.error(`4. 从 https://nodejs.org/ 下载并安装 Node.js ${requiredVersion}`);
92
+  console.error(`1. 如果已安装 nvm: 运行 'nvm install ${preferredVersion} && nvm use ${preferredVersion}'`);
93
+  console.error(`2. 使用项目提供的辅助脚本: './scripts/use-node-version.sh node -v'`);
94
+  console.error(`3. 从 https://nodejs.org/ 下载并安装 Node.js ${preferredVersion}`);
55 95
   
56 96
   process.exit(1);
57 97
 }
58 98
 
59
-// 检查是否已安装 nvm
60
-try {
61
-  execSync('command -v nvm', { stdio: 'ignore' });
62
-  // 如果 nvm 可用,尝试自动切换版本
63
-  try {
64
-    execSync(`. "${process.env.NVM_DIR}/nvm.sh" && nvm use ${requiredVersion}`, { 
65
-      stdio: 'inherit',
66
-      shell: '/bin/bash'
67
-    });
68
-  } catch (error) {
69
-    // 如果自动切换失败,继续执行,因为当前版本已经匹配
70
-  }
71
-} catch (error) {
72
-  // nvm 未安装,但当前版本已经匹配,所以继续执行
73
-}
74
-
75
-console.log(`\n✓ Node.js 版本检查通过! (${currentVersion})`);
99
+console.log(`\n✓ Node.js 版本检查通过! (${currentVersion})`);

+ 17 - 21
scripts/use-node-version.sh

@@ -1,28 +1,24 @@
1 1
 #!/bin/bash
2
+set -e
2 3
 
3
-# 检查是否安装了 nvm
4
-if [ -z "$(command -v nvm)" ]; then
5
-  echo "Error: nvm is not installed"
6
-  echo "Please install nvm first: https://github.com/nvm-sh/nvm#installing-and-updating"
7
-  exit 1
8
-fi
9
-
10
-# 获取 .nvmrc 中指定的版本
11
-NODE_VERSION=$(cat .nvmrc)
4
+PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
5
+NODE_VERSION="$(cat "$PROJECT_DIR/.nvmrc")"
6
+NVM_SCRIPT="${NVM_DIR:-$HOME/.nvm}/nvm.sh"
12 7
 
13
-# 检查是否已安装所需版本
14
-if ! nvm ls $NODE_VERSION > /dev/null 2>&1; then
15
-  echo "Required Node.js version $NODE_VERSION is not installed"
16
-  echo "Installing Node.js $NODE_VERSION..."
17
-  nvm install $NODE_VERSION
8
+if [ -s "$NVM_SCRIPT" ]; then
9
+  . "$NVM_SCRIPT"
10
+  if ! nvm ls "$NODE_VERSION" > /dev/null 2>&1; then
11
+    echo "Required Node.js version $NODE_VERSION is not installed"
12
+    echo "Installing Node.js $NODE_VERSION..."
13
+    nvm install "$NODE_VERSION"
14
+  fi
15
+  echo "Switching to Node.js $NODE_VERSION..."
16
+  nvm use "$NODE_VERSION"
17
+else
18
+  echo "nvm is not available; validating the current Node.js version instead."
19
+  node "$PROJECT_DIR/scripts/check-node-version.js"
18 20
 fi
19 21
 
20
-# 使用指定版本
21
-echo "Switching to Node.js $NODE_VERSION..."
22
-. "$(dirname $(command -v nvm))/nvm.sh" # 加载 nvm
23
-nvm use $NODE_VERSION
24
-
25
-# 执行传入的命令
26 22
 if [ $# -gt 0 ]; then
27 23
   exec "$@"
28
-fi
24
+fi

+ 3 - 5
src/util/stringClass.js

@@ -1,6 +1,7 @@
1 1
 'use strict';
2 2
 import moment from 'moment';
3 3
 import os from 'os';
4
+import iconv from 'iconv-lite';
4 5
 
5 6
 /**
6 7
  * 字符串工具类集合
@@ -410,15 +411,12 @@ export const stringUtils = {
410 411
     },
411 412
     //转码
412 413
     SetEncode: (content, encode) => {
413
-        const buffer = new Buffer(content);
414
-        //需要转换字符集
415
-        const iconv = require('iconv-lite');
414
+        const buffer = Buffer.from(content);
416 415
         return iconv.encode(buffer, encode);
417 416
     },
418 417
     //解码
419 418
     SetDecode: (content, encode) => {
420
-        const buffer = new Buffer(content);
421
-        const iconv = require('iconv-lite');
419
+        const buffer = Buffer.from(content);
422 420
         return iconv.decode(buffer, encode);
423 421
     },
424 422
     //将JSON转换为XML格式

+ 136 - 0
秒过分数线数据导入/fix_mps_score_qingpu_yizhong_shangmei_2026.py

@@ -0,0 +1,136 @@
1
+import json
2
+from decimal import Decimal
3
+
4
+import pymysql
5
+
6
+import import_mps_score_all_district_scores_2026 as importer
7
+
8
+
9
+DISTRICT_ID = 14
10
+SCORE_TYPE = "名额到校"
11
+TARGET_IDS = {39138, 39153, 39169, 39184, 39200, 39215}
12
+BACKUP_FILE = "mps_score_qingpu_yizhong_shangmei_2026_backup.json"
13
+
14
+
15
+def decimal_to_json(value):
16
+    if isinstance(value, Decimal):
17
+        return str(value)
18
+    return value
19
+
20
+
21
+def collect_updates(cursor):
22
+    names_by_school_id = importer.load_school_names(cursor)
23
+    data = importer.collect_one(cursor, names_by_school_id, DISTRICT_ID, SCORE_TYPE)
24
+    if data["problems"] or data["duplicates"]:
25
+        raise RuntimeError(
26
+            f"Qingpu match still has problems={len(data['problems'])}, "
27
+            f"duplicates={len(data['duplicates'])}"
28
+        )
29
+    updates = [row for row in data["updates"] if row["ID"] in TARGET_IDS]
30
+    found_ids = {row["ID"] for row in updates}
31
+    if found_ids != TARGET_IDS:
32
+        raise RuntimeError(f"missing target updates: {sorted(TARGET_IDS - found_ids)}")
33
+    return sorted(updates, key=lambda row: row["ID"])
34
+
35
+
36
+def fetch_rows(cursor, ids):
37
+    placeholders = ", ".join(["%s"] * len(ids))
38
+    cursor.execute(
39
+        f"""
40
+        SELECT ID, ScoreYear, ScoreType, DistrictID, SchoolOfGraduation,
41
+               SchoolFullNameJunior, SchoolTarget, SchoolFullName, PlanNum,
42
+               ScoreTotal, Score1, Score2, Score3, Score4, ScoreTotalDifferenceValue
43
+        FROM MPS_Score
44
+        WHERE ID IN ({placeholders})
45
+        ORDER BY ID
46
+        """,
47
+        sorted(ids),
48
+    )
49
+    return cursor.fetchall()
50
+
51
+
52
+def update_scores(cursor, updates):
53
+    changed = 0
54
+    for row in updates:
55
+        cursor.execute(
56
+            """
57
+            UPDATE MPS_Score
58
+            SET ScoreTotal = %s,
59
+                Score1 = %s,
60
+                Score2 = %s,
61
+                Score3 = %s,
62
+                Score4 = %s,
63
+                ScoreTotalDifferenceValue = %s
64
+            WHERE ID = %s
65
+              AND ScoreYear = %s
66
+              AND ScoreType = %s
67
+              AND DistrictID = %s
68
+            """,
69
+            (
70
+                row["ScoreTotal"],
71
+                row["Score1"],
72
+                row["Score2"],
73
+                row["Score3"],
74
+                row["Score4"],
75
+                row["ScoreTotalDifferenceValue"],
76
+                row["ID"],
77
+                importer.YEAR,
78
+                SCORE_TYPE,
79
+                DISTRICT_ID,
80
+            ),
81
+        )
82
+        if cursor.rowcount != 1:
83
+            raise RuntimeError(f"unexpected rowcount for ID {row['ID']}: {cursor.rowcount}")
84
+        changed += cursor.rowcount
85
+    return changed
86
+
87
+
88
+def verify(cursor, updates):
89
+    actual_by_id = {row["ID"]: row for row in fetch_rows(cursor, TARGET_IDS)}
90
+    mismatches = []
91
+    for row in updates:
92
+        actual = actual_by_id[row["ID"]]
93
+        for column in importer.SCORE_COLUMNS:
94
+            if actual[column] != row[column]:
95
+                mismatches.append((row["ID"], column, actual[column], row[column]))
96
+    if mismatches:
97
+        raise RuntimeError(f"verification failed: {mismatches[:10]}")
98
+
99
+
100
+def main():
101
+    conn = pymysql.connect(**importer.DB_CONFIG)
102
+    try:
103
+        with conn.cursor() as cursor:
104
+            updates = collect_updates(cursor)
105
+            backup_rows = fetch_rows(cursor, TARGET_IDS)
106
+            with open(BACKUP_FILE, "w", encoding="utf-8") as handle:
107
+                json.dump(backup_rows, handle, ensure_ascii=False, indent=2, default=decimal_to_json)
108
+
109
+            for row in updates:
110
+                print(
111
+                    "update",
112
+                    row["ID"],
113
+                    row["SchoolFullNameJunior"],
114
+                    "->",
115
+                    row["SchoolFullName"],
116
+                    "pdf_total",
117
+                    row["total_with_eval"],
118
+                    "score",
119
+                    row["ScoreTotal"],
120
+                    "diff",
121
+                    row["ScoreTotalDifferenceValue"],
122
+                )
123
+            changed = update_scores(cursor, updates)
124
+            verify(cursor, updates)
125
+            conn.commit()
126
+            print("updated", changed)
127
+            print("backup", BACKUP_FILE)
128
+    except Exception:
129
+        conn.rollback()
130
+        raise
131
+    finally:
132
+        conn.close()
133
+
134
+
135
+if __name__ == "__main__":
136
+    main()

+ 170 - 0
秒过分数线数据导入/fix_mps_score_scores_2026_tie_preferred.py

@@ -0,0 +1,170 @@
1
+import json
2
+from decimal import Decimal
3
+
4
+import pymysql
5
+
6
+import import_mps_score_all_district_scores_2026 as importer
7
+
8
+
9
+BACKUP_FILE = "mps_score_scores_2026_tie_preferred_backup.json"
10
+
11
+
12
+def decimal_to_json(value):
13
+    if isinstance(value, Decimal):
14
+        return str(value)
15
+    return value
16
+
17
+
18
+def collect_tie_updates(cursor):
19
+    names_by_school_id = importer.load_school_names(cursor)
20
+    updates = []
21
+    problems = []
22
+    duplicates = []
23
+
24
+    for district_id in sorted(importer.DISTRICTS):
25
+        for score_type in importer.SCORE_TYPES:
26
+            parsed_rows = [
27
+                row
28
+                for row in importer.parse_pdf(score_type, importer.pdf_path(score_type, district_id))
29
+                if row.get("is_tie_preferred")
30
+            ]
31
+            if not parsed_rows:
32
+                continue
33
+
34
+            db_rows = importer.fetch_existing(cursor, score_type, district_id, importer.YEAR)
35
+            previous_scores = importer.previous_scores_by_key(cursor, score_type, district_id)
36
+            matched, match_problems, duplicate_rows, _missing_db = importer.attach_matches(
37
+                score_type, parsed_rows, db_rows, names_by_school_id
38
+            )
39
+            if match_problems:
40
+                problems.extend(
41
+                    {
42
+                        "district_id": district_id,
43
+                        "district": importer.DISTRICTS[district_id],
44
+                        "score_type": score_type,
45
+                        "problem": problem,
46
+                    }
47
+                    for problem in match_problems
48
+                )
49
+            if duplicate_rows:
50
+                duplicates.extend(
51
+                    {
52
+                        "district_id": district_id,
53
+                        "district": importer.DISTRICTS[district_id],
54
+                        "score_type": score_type,
55
+                        "db_id": db_id,
56
+                    }
57
+                    for db_id in duplicate_rows
58
+                )
59
+            updates.extend(importer.build_updates(score_type, matched, previous_scores))
60
+
61
+    if problems or duplicates:
62
+        raise RuntimeError(f"tie row match failed: problems={problems[:3]}, duplicates={duplicates[:3]}")
63
+    return updates
64
+
65
+
66
+def fetch_backup_rows(cursor, ids):
67
+    if not ids:
68
+        return []
69
+    placeholders = ", ".join(["%s"] * len(ids))
70
+    cursor.execute(
71
+        f"""
72
+        SELECT ID, ScoreYear, ScoreType, DistrictID, SchoolOfGraduation,
73
+               SchoolFullNameJunior, SchoolTarget, SchoolFullName, PlanNum,
74
+               ScoreTotal, Score1, Score2, Score3, Score4, ScoreTotalDifferenceValue
75
+        FROM MPS_Score
76
+        WHERE ID IN ({placeholders})
77
+        ORDER BY ID
78
+        """,
79
+        ids,
80
+    )
81
+    return cursor.fetchall()
82
+
83
+
84
+def update_rows(cursor, updates):
85
+    changed = 0
86
+    for update in updates:
87
+        score_type = "名额到校" if update["SchoolOfGraduation"] not in (None, 0) else "名额到区"
88
+        cursor.execute(
89
+            """
90
+            UPDATE MPS_Score
91
+            SET ScoreTotal = %s,
92
+                Score1 = %s,
93
+                Score2 = %s,
94
+                Score3 = %s,
95
+                Score4 = %s,
96
+                ScoreTotalDifferenceValue = %s
97
+            WHERE ID = %s
98
+              AND ScoreYear = %s
99
+              AND ScoreType = %s
100
+              AND DistrictID = %s
101
+            """,
102
+            (
103
+                update["ScoreTotal"],
104
+                update["Score1"],
105
+                update["Score2"],
106
+                update["Score3"],
107
+                update["Score4"],
108
+                update["ScoreTotalDifferenceValue"],
109
+                update["ID"],
110
+                importer.YEAR,
111
+                score_type,
112
+                update["DistrictID"],
113
+            ),
114
+        )
115
+        if cursor.rowcount != 1:
116
+            raise RuntimeError(f"unexpected rowcount for ID {update['ID']}: {cursor.rowcount}")
117
+        changed += cursor.rowcount
118
+    return changed
119
+
120
+
121
+def verify(cursor, updates):
122
+    ids = [update["ID"] for update in updates]
123
+    actual_by_id = {row["ID"]: row for row in fetch_backup_rows(cursor, ids)}
124
+    mismatches = []
125
+    for update in updates:
126
+        actual = actual_by_id.get(update["ID"])
127
+        for column in importer.SCORE_COLUMNS:
128
+            if actual[column] != update[column]:
129
+                mismatches.append((update["ID"], column, actual[column], update[column]))
130
+    if mismatches:
131
+        raise RuntimeError(f"verification failed: {mismatches[:10]}")
132
+
133
+
134
+def main():
135
+    conn = pymysql.connect(**importer.DB_CONFIG)
136
+    try:
137
+        with conn.cursor() as cursor:
138
+            updates = collect_tie_updates(cursor)
139
+            ids = [update["ID"] for update in updates]
140
+            backup_rows = fetch_backup_rows(cursor, ids)
141
+            with open(BACKUP_FILE, "w", encoding="utf-8") as handle:
142
+                json.dump(backup_rows, handle, ensure_ascii=False, indent=2, default=decimal_to_json)
143
+
144
+            for update in updates:
145
+                print(
146
+                    "tie_update",
147
+                    update["ID"],
148
+                    update["DistrictID"],
149
+                    update["SchoolFullNameJunior"],
150
+                    "->",
151
+                    update["SchoolFullName"],
152
+                    "score",
153
+                    update["ScoreTotal"],
154
+                    "diff",
155
+                    update["ScoreTotalDifferenceValue"],
156
+                )
157
+            changed = update_rows(cursor, updates)
158
+            verify(cursor, updates)
159
+            conn.commit()
160
+            print("updated", changed)
161
+            print("backup", BACKUP_FILE)
162
+    except Exception:
163
+        conn.rollback()
164
+        raise
165
+    finally:
166
+        conn.close()
167
+
168
+
169
+if __name__ == "__main__":
170
+    main()

+ 31 - 11
秒过分数线数据导入/import_mps_score_all_district_scores_2026.py

@@ -57,6 +57,10 @@ SCORE_COLUMNS = [
57 57
     "ScoreTotalDifferenceValue",
58 58
 ]
59 59
 
60
+MANUAL_SCHOOL_ALIASES = {
61
+    752: ["上海市青浦区第一中学尚美分校"],
62
+}
63
+
60 64
 
61 65
 def compact(value):
62 66
     return re.sub(r"\s+", "", str(value or "").replace("\u3000", ""))
@@ -170,24 +174,35 @@ def parse_pdf(score_type, path):
170 174
                         continue
171 175
 
172 176
                     total_with_eval = parse_decimal(row[2])
173
-                    eval_score = parse_decimal(row[4])
174
-                    score1 = parse_decimal(row[5])
175
-                    score2 = parse_decimal(row[6])
176
-                    score3 = parse_decimal(row[7])
177
-                    score4 = parse_decimal(row[8])
178
-                    if None in (total_with_eval, eval_score, score1, score2, score3, score4):
177
+                    if total_with_eval is None:
179 178
                         continue
180
-                    if eval_score != Decimal("50"):
181
-                        raise ValueError(
182
-                            f"{score_type} page {page_number} row {row_index} "
183
-                            f"has unexpected 综合素质评价 score: {eval_score}"
184
-                        )
179
+
180
+                    is_tie_preferred = "是" in compact(row[3])
181
+                    if is_tie_preferred:
182
+                        score1 = Decimal("0")
183
+                        score2 = Decimal("0")
184
+                        score3 = Decimal("0")
185
+                        score4 = Decimal("0")
186
+                    else:
187
+                        eval_score = parse_decimal(row[4])
188
+                        score1 = parse_decimal(row[5])
189
+                        score2 = parse_decimal(row[6])
190
+                        score3 = parse_decimal(row[7])
191
+                        score4 = parse_decimal(row[8])
192
+                        if None in (eval_score, score1, score2, score3, score4):
193
+                            continue
194
+                        if eval_score != Decimal("50"):
195
+                            raise ValueError(
196
+                                f"{score_type} page {page_number} row {row_index} "
197
+                                f"has unexpected 综合素质评价 score: {eval_score}"
198
+                            )
185 199
 
186 200
                     parsed = {
187 201
                         "page": page_number,
188 202
                         "row_index": row_index,
189 203
                         "raw": row,
190 204
                         "total_with_eval": total_with_eval,
205
+                        "is_tie_preferred": is_tie_preferred,
191 206
                         "ScoreTotal": total_with_eval - Decimal("50"),
192 207
                         "Score1": score1,
193 208
                         "Score2": score2,
@@ -220,6 +235,11 @@ def load_school_names(cursor):
220 235
         for alias in split_aliases(row["SchoolOtherName"]):
221 236
             if alias not in names_by_id[int(row["ID"])]:
222 237
                 names_by_id[int(row["ID"])].append(alias)
238
+    for school_id, aliases in MANUAL_SCHOOL_ALIASES.items():
239
+        for alias in aliases:
240
+            alias = compact(alias)
241
+            if alias and alias not in names_by_id[school_id]:
242
+                names_by_id[school_id].append(alias)
223 243
     return names_by_id
224 244
 
225 245
 

+ 104 - 0
秒过分数线数据导入/mps_score_qingpu_yizhong_shangmei_2026_backup.json

@@ -0,0 +1,104 @@
1
+[
2
+  {
3
+    "ID": 39138,
4
+    "ScoreYear": "2026",
5
+    "ScoreType": "名额到校",
6
+    "DistrictID": 14,
7
+    "SchoolOfGraduation": 752,
8
+    "SchoolFullNameJunior": "上海市青浦区尚美中学",
9
+    "SchoolTarget": "69",
10
+    "SchoolFullName": "复旦大学附属中学青浦分校",
11
+    "PlanNum": 3,
12
+    "ScoreTotal": "0.00",
13
+    "Score1": "0.00",
14
+    "Score2": "0.00",
15
+    "Score3": "0.00",
16
+    "Score4": "0.00",
17
+    "ScoreTotalDifferenceValue": "0.00"
18
+  },
19
+  {
20
+    "ID": 39153,
21
+    "ScoreYear": "2026",
22
+    "ScoreType": "名额到校",
23
+    "DistrictID": 14,
24
+    "SchoolOfGraduation": 766,
25
+    "SchoolFullNameJunior": "上海市青浦区第一中学",
26
+    "SchoolTarget": "69",
27
+    "SchoolFullName": "复旦大学附属中学青浦分校",
28
+    "PlanNum": 9,
29
+    "ScoreTotal": "0.00",
30
+    "Score1": "0.00",
31
+    "Score2": "0.00",
32
+    "Score3": "0.00",
33
+    "Score4": "0.00",
34
+    "ScoreTotalDifferenceValue": "0.00"
35
+  },
36
+  {
37
+    "ID": 39169,
38
+    "ScoreYear": "2026",
39
+    "ScoreType": "名额到校",
40
+    "DistrictID": 14,
41
+    "SchoolOfGraduation": 752,
42
+    "SchoolFullNameJunior": "上海市青浦区尚美中学",
43
+    "SchoolTarget": "67",
44
+    "SchoolFullName": "上海市青浦高级中学",
45
+    "PlanNum": 6,
46
+    "ScoreTotal": "0.00",
47
+    "Score1": "0.00",
48
+    "Score2": "0.00",
49
+    "Score3": "0.00",
50
+    "Score4": "0.00",
51
+    "ScoreTotalDifferenceValue": "0.00"
52
+  },
53
+  {
54
+    "ID": 39184,
55
+    "ScoreYear": "2026",
56
+    "ScoreType": "名额到校",
57
+    "DistrictID": 14,
58
+    "SchoolOfGraduation": 766,
59
+    "SchoolFullNameJunior": "上海市青浦区第一中学",
60
+    "SchoolTarget": "67",
61
+    "SchoolFullName": "上海市青浦高级中学",
62
+    "PlanNum": 20,
63
+    "ScoreTotal": "0.00",
64
+    "Score1": "0.00",
65
+    "Score2": "0.00",
66
+    "Score3": "0.00",
67
+    "Score4": "0.00",
68
+    "ScoreTotalDifferenceValue": "0.00"
69
+  },
70
+  {
71
+    "ID": 39200,
72
+    "ScoreYear": "2026",
73
+    "ScoreType": "名额到校",
74
+    "DistrictID": 14,
75
+    "SchoolOfGraduation": 752,
76
+    "SchoolFullNameJunior": "上海市青浦区尚美中学",
77
+    "SchoolTarget": "68",
78
+    "SchoolFullName": "上海市朱家角中学",
79
+    "PlanNum": 6,
80
+    "ScoreTotal": "0.00",
81
+    "Score1": "0.00",
82
+    "Score2": "0.00",
83
+    "Score3": "0.00",
84
+    "Score4": "0.00",
85
+    "ScoreTotalDifferenceValue": "0.00"
86
+  },
87
+  {
88
+    "ID": 39215,
89
+    "ScoreYear": "2026",
90
+    "ScoreType": "名额到校",
91
+    "DistrictID": 14,
92
+    "SchoolOfGraduation": 766,
93
+    "SchoolFullNameJunior": "上海市青浦区第一中学",
94
+    "SchoolTarget": "68",
95
+    "SchoolFullName": "上海市朱家角中学",
96
+    "PlanNum": 19,
97
+    "ScoreTotal": "0.00",
98
+    "Score1": "0.00",
99
+    "Score2": "0.00",
100
+    "Score3": "0.00",
101
+    "Score4": "0.00",
102
+    "ScoreTotalDifferenceValue": "0.00"
103
+  }
104
+]

+ 24 - 228
秒过分数线数据导入/mps_score_scores_2026_import_report.json

@@ -391,27 +391,18 @@
391 391
       "district_id": 5,
392 392
       "district": "普陀区",
393 393
       "score_type": "名额到区",
394
-      "parsed": 65,
394
+      "parsed": 66,
395 395
       "db": 66,
396
-      "matched_for_update": 65,
396
+      "matched_for_update": 66,
397 397
       "problems": 0,
398 398
       "duplicate_target_rows": 0,
399
-      "missing_from_pdf_or_skipped": 1,
399
+      "missing_from_pdf_or_skipped": 0,
400 400
       "min_score_after_minus_50": "641.5",
401 401
       "max_score_after_minus_50": "719.5"
402 402
     },
403 403
     "problems": [],
404 404
     "duplicates": [],
405
-    "missing_from_pdf_or_skipped": [
406
-      {
407
-        "ID": 32299,
408
-        "SchoolOfGraduation": 0,
409
-        "SchoolFullNameJunior": null,
410
-        "SchoolTarget": "55",
411
-        "SchoolFullName": "上海市进才中学",
412
-        "PlanNum": 5
413
-      }
414
-    ]
405
+    "missing_from_pdf_or_skipped": []
415 406
   },
416 407
   {
417 408
     "summary": {
@@ -505,53 +496,36 @@
505 496
       "district_id": 7,
506 497
       "district": "杨浦区",
507 498
       "score_type": "名额到区",
508
-      "parsed": 66,
499
+      "parsed": 67,
509 500
       "db": 67,
510
-      "matched_for_update": 66,
501
+      "matched_for_update": 67,
511 502
       "problems": 0,
512 503
       "duplicate_target_rows": 0,
513
-      "missing_from_pdf_or_skipped": 1,
504
+      "missing_from_pdf_or_skipped": 0,
514 505
       "min_score_after_minus_50": "637",
515 506
       "max_score_after_minus_50": "718.5"
516 507
     },
517 508
     "problems": [],
518 509
     "duplicates": [],
519
-    "missing_from_pdf_or_skipped": [
520
-      {
521
-        "ID": 32318,
522
-        "SchoolOfGraduation": 0,
523
-        "SchoolFullNameJunior": null,
524
-        "SchoolTarget": "2",
525
-        "SchoolFullName": "上海交通大学附属中学",
526
-        "PlanNum": 47
527
-      }
528
-    ]
510
+    "missing_from_pdf_or_skipped": []
529 511
   },
530 512
   {
531 513
     "summary": {
532 514
       "district_id": 7,
533 515
       "district": "杨浦区",
534 516
       "score_type": "名额到校",
535
-      "parsed": 140,
517
+      "parsed": 141,
536 518
       "db": 144,
537
-      "matched_for_update": 140,
519
+      "matched_for_update": 141,
538 520
       "problems": 0,
539 521
       "duplicate_target_rows": 0,
540
-      "missing_from_pdf_or_skipped": 4,
522
+      "missing_from_pdf_or_skipped": 3,
541 523
       "min_score_after_minus_50": "649.5",
542 524
       "max_score_after_minus_50": "718"
543 525
     },
544 526
     "problems": [],
545 527
     "duplicates": [],
546 528
     "missing_from_pdf_or_skipped": [
547
-      {
548
-        "ID": 33669,
549
-        "SchoolOfGraduation": 425,
550
-        "SchoolFullNameJunior": "上海市铁岭中学",
551
-        "SchoolTarget": "40",
552
-        "SchoolFullName": "上海市控江中学",
553
-        "PlanNum": 14
554
-      },
555 529
       {
556 530
         "ID": 33772,
557 531
         "SchoolOfGraduation": 838,
@@ -601,26 +575,18 @@
601 575
       "district_id": 8,
602 576
       "district": "闵行区",
603 577
       "score_type": "名额到校",
604
-      "parsed": 437,
578
+      "parsed": 438,
605 579
       "db": 460,
606
-      "matched_for_update": 437,
580
+      "matched_for_update": 438,
607 581
       "problems": 0,
608 582
       "duplicate_target_rows": 0,
609
-      "missing_from_pdf_or_skipped": 23,
583
+      "missing_from_pdf_or_skipped": 22,
610 584
       "min_score_after_minus_50": "641",
611 585
       "max_score_after_minus_50": "721"
612 586
     },
613 587
     "problems": [],
614 588
     "duplicates": [],
615 589
     "missing_from_pdf_or_skipped": [
616
-      {
617
-        "ID": 34049,
618
-        "SchoolOfGraduation": 487,
619
-        "SchoolFullNameJunior": "上海市七宝中学附属鑫都实验中学",
620
-        "SchoolTarget": "44",
621
-        "SchoolFullName": "华东师范大学第二附属中学闵行紫竹分校",
622
-        "PlanNum": 7
623
-      },
624 590
       {
625 591
         "ID": 34214,
626 592
         "SchoolOfGraduation": 914,
@@ -822,12 +788,12 @@
822 788
       "district_id": 9,
823 789
       "district": "宝山区",
824 790
       "score_type": "名额到校",
825
-      "parsed": 347,
791
+      "parsed": 348,
826 792
       "db": 343,
827
-      "matched_for_update": 322,
793
+      "matched_for_update": 323,
828 794
       "problems": 0,
829 795
       "duplicate_target_rows": 10,
830
-      "missing_from_pdf_or_skipped": 21,
796
+      "missing_from_pdf_or_skipped": 20,
831 797
       "min_score_after_minus_50": "617.5",
832 798
       "max_score_after_minus_50": "715"
833 799
     },
@@ -1260,14 +1226,6 @@
1260 1226
       }
1261 1227
     ],
1262 1228
     "missing_from_pdf_or_skipped": [
1263
-      {
1264
-        "ID": 37011,
1265
-        "SchoolOfGraduation": 500,
1266
-        "SchoolFullNameJunior": "上海市淞谊中学",
1267
-        "SchoolTarget": "49",
1268
-        "SchoolFullName": "上海市吴淞中学",
1269
-        "PlanNum": 7
1270
-      },
1271 1229
       {
1272 1230
         "ID": 37025,
1273 1231
         "SchoolOfGraduation": 503,
@@ -2693,12 +2651,12 @@
2693 2651
       "district_id": 13,
2694 2652
       "district": "松江区",
2695 2653
       "score_type": "名额到校",
2696
-      "parsed": 201,
2654
+      "parsed": 202,
2697 2655
       "db": 190,
2698
-      "matched_for_update": 176,
2656
+      "matched_for_update": 177,
2699 2657
       "problems": 19,
2700 2658
       "duplicate_target_rows": 3,
2701
-      "missing_from_pdf_or_skipped": 14,
2659
+      "missing_from_pdf_or_skipped": 13,
2702 2660
       "min_score_after_minus_50": "637",
2703 2661
       "max_score_after_minus_50": "715"
2704 2662
     },
@@ -3143,14 +3101,6 @@
3143 3101
         "SchoolFullName": "上海师范大学附属中学松江分校",
3144 3102
         "PlanNum": 1
3145 3103
       },
3146
-      {
3147
-        "ID": 38643,
3148
-        "SchoolOfGraduation": 1019,
3149
-        "SchoolFullNameJunior": "上海外国语大学附属外国语学校松江云间中学",
3150
-        "SchoolTarget": "63",
3151
-        "SchoolFullName": "上海市松江二中",
3152
-        "PlanNum": 6
3153
-      },
3154 3104
       {
3155 3105
         "ID": 38668,
3156 3106
         "SchoolOfGraduation": 728,
@@ -3274,170 +3224,16 @@
3274 3224
       "score_type": "名额到校",
3275 3225
       "parsed": 98,
3276 3226
       "db": 99,
3277
-      "matched_for_update": 92,
3227
+      "matched_for_update": 98,
3278 3228
       "problems": 0,
3279
-      "duplicate_target_rows": 3,
3280
-      "missing_from_pdf_or_skipped": 7,
3229
+      "duplicate_target_rows": 0,
3230
+      "missing_from_pdf_or_skipped": 1,
3281 3231
       "min_score_after_minus_50": "619",
3282 3232
       "max_score_after_minus_50": "717.5"
3283 3233
     },
3284 3234
     "problems": [],
3285
-    "duplicates": [
3286
-      {
3287
-        "db_id": 39184,
3288
-        "rows": [
3289
-          {
3290
-            "page": 1,
3291
-            "row_index": 9,
3292
-            "raw": [
3293
-              "上海市青浦区第一中学尚美分校",
3294
-              "市\n上海市青浦高级中学",
3295
-              "院\n720",
3296
-              "否",
3297
-              "50",
3298
-              "393.5",
3299
-              "132",
3300
-              "127",
3301
-              "141"
3302
-            ]
3303
-          },
3304
-          {
3305
-            "page": 2,
3306
-            "row_index": 30,
3307
-            "raw": [
3308
-              "上海市青浦区第一中学",
3309
-              "上海市青浦高级中学",
3310
-              "728.5",
3311
-              "否",
3312
-              "市\n50",
3313
-              "397.5",
3314
-              "136",
3315
-              "128",
3316
-              "137"
3317
-            ]
3318
-          }
3319
-        ]
3320
-      },
3321
-      {
3322
-        "db_id": 39153,
3323
-        "rows": [
3324
-          {
3325
-            "page": 1,
3326
-            "row_index": 10,
3327
-            "raw": [
3328
-              "上海市青浦区第一中学尚美分校",
3329
-              "海\n复旦大学附属中学青浦分校",
3330
-              "试\n737",
3331
-              "否",
3332
-              "50",
3333
-              "404.5",
3334
-              "148",
3335
-              "118",
3336
-              "上\n142"
3337
-            ]
3338
-          },
3339
-          {
3340
-            "page": 2,
3341
-            "row_index": 31,
3342
-            "raw": [
3343
-              "上海市青浦区第一中学",
3344
-              "院\n试\n复旦大学附属中学青浦分校",
3345
-              "752.5",
3346
-              "海\n否",
3347
-              "50",
3348
-              "418.5",
3349
-              "142",
3350
-              "133",
3351
-              "139"
3352
-            ]
3353
-          }
3354
-        ]
3355
-      },
3356
-      {
3357
-        "db_id": 39215,
3358
-        "rows": [
3359
-          {
3360
-            "page": 1,
3361
-            "row_index": 11,
3362
-            "raw": [
3363
-              "上\n上海市青浦区第一中学尚美分校",
3364
-              "上海市朱家角中学",
3365
-              "考\n育\n694.5",
3366
-              "否",
3367
-              "50",
3368
-              "379.5",
3369
-              "119",
3370
-              "127",
3371
-              "115"
3372
-            ]
3373
-          },
3374
-          {
3375
-            "page": 3,
3376
-            "row_index": 2,
3377
-            "raw": [
3378
-              "育\n上海市青浦区第一中学",
3379
-              "考\n上海市朱家角中学",
3380
-              "709.5",
3381
-              "否",
3382
-              "50",
3383
-              "399.5",
3384
-              "142",
3385
-              "122",
3386
-              "123"
3387
-            ]
3388
-          }
3389
-        ]
3390
-      }
3391
-    ],
3235
+    "duplicates": [],
3392 3236
     "missing_from_pdf_or_skipped": [
3393
-      {
3394
-        "ID": 39138,
3395
-        "SchoolOfGraduation": 752,
3396
-        "SchoolFullNameJunior": "上海市青浦区尚美中学",
3397
-        "SchoolTarget": "69",
3398
-        "SchoolFullName": "复旦大学附属中学青浦分校",
3399
-        "PlanNum": 3
3400
-      },
3401
-      {
3402
-        "ID": 39153,
3403
-        "SchoolOfGraduation": 766,
3404
-        "SchoolFullNameJunior": "上海市青浦区第一中学",
3405
-        "SchoolTarget": "69",
3406
-        "SchoolFullName": "复旦大学附属中学青浦分校",
3407
-        "PlanNum": 9
3408
-      },
3409
-      {
3410
-        "ID": 39169,
3411
-        "SchoolOfGraduation": 752,
3412
-        "SchoolFullNameJunior": "上海市青浦区尚美中学",
3413
-        "SchoolTarget": "67",
3414
-        "SchoolFullName": "上海市青浦高级中学",
3415
-        "PlanNum": 6
3416
-      },
3417
-      {
3418
-        "ID": 39184,
3419
-        "SchoolOfGraduation": 766,
3420
-        "SchoolFullNameJunior": "上海市青浦区第一中学",
3421
-        "SchoolTarget": "67",
3422
-        "SchoolFullName": "上海市青浦高级中学",
3423
-        "PlanNum": 20
3424
-      },
3425
-      {
3426
-        "ID": 39200,
3427
-        "SchoolOfGraduation": 752,
3428
-        "SchoolFullNameJunior": "上海市青浦区尚美中学",
3429
-        "SchoolTarget": "68",
3430
-        "SchoolFullName": "上海市朱家角中学",
3431
-        "PlanNum": 6
3432
-      },
3433
-      {
3434
-        "ID": 39215,
3435
-        "SchoolOfGraduation": 766,
3436
-        "SchoolFullNameJunior": "上海市青浦区第一中学",
3437
-        "SchoolTarget": "68",
3438
-        "SchoolFullName": "上海市朱家角中学",
3439
-        "PlanNum": 19
3440
-      },
3441 3237
       {
3442 3238
         "ID": 39225,
3443 3239
         "SchoolOfGraduation": 977,

+ 294 - 294
秒过分数线数据导入/mps_score_scores_2026_sample_audit.json

@@ -790,24 +790,24 @@
790 790
       "district_id": 5,
791 791
       "district": "普陀区",
792 792
       "score_type": "名额到区",
793
-      "pdf_parsed_rows": 65,
793
+      "pdf_parsed_rows": 66,
794 794
       "db_rows": 66,
795
-      "auditable_imported_rows": 65,
795
+      "auditable_imported_rows": 66,
796 796
       "sample_rate": "0.30",
797 797
       "sample_size": 20,
798 798
       "sample_errors": [],
799 799
       "full_recheck_triggered": false,
800 800
       "full_recheck_errors": [],
801
-      "not_audited_pending_rows": 1,
801
+      "not_audited_pending_rows": 0,
802 802
       "pending_match_problems": 0,
803 803
       "pending_duplicate_target_rows": 0,
804 804
       "sample_preview": [
805 805
         {
806
-          "ID": 32254,
806
+          "ID": 32299,
807 807
           "SchoolFullNameJunior": null,
808
-          "SchoolFullName": "华东师范大学第二附属中学",
809
-          "pdf_total": "767",
810
-          "stored_score_total": "717",
808
+          "SchoolFullName": "上海市进才中学",
809
+          "pdf_total": "750.5",
810
+          "stored_score_total": "700.5",
811 811
           "page": 2
812 812
         },
813 813
         {
@@ -819,12 +819,12 @@
819 819
           "page": 1
820 820
         },
821 821
         {
822
-          "ID": 32290,
822
+          "ID": 32311,
823 823
           "SchoolFullNameJunior": null,
824
-          "SchoolFullName": "上海市行知中学",
825
-          "pdf_total": "737",
826
-          "stored_score_total": "687",
827
-          "page": 2
824
+          "SchoolFullName": "上海市青浦高级中学",
825
+          "pdf_total": "736.5",
826
+          "stored_score_total": "686.5",
827
+          "page": 3
828 828
         },
829 829
         {
830 830
           "ID": 32305,
@@ -835,35 +835,11 @@
835 835
           "page": 2
836 836
         },
837 837
         {
838
-          "ID": 32313,
838
+          "ID": 32302,
839 839
           "SchoolFullNameJunior": null,
840
-          "SchoolFullName": "复旦大学附属中学青浦分校",
841
-          "pdf_total": "764",
842
-          "stored_score_total": "714",
843
-          "page": 3
844
-        },
845
-        {
846
-          "ID": 32285,
847
-          "SchoolFullNameJunior": null,
848
-          "SchoolFullName": "上海市控江中学",
849
-          "pdf_total": "752",
850
-          "stored_score_total": "702",
851
-          "page": 2
852
-        },
853
-        {
854
-          "ID": 32255,
855
-          "SchoolFullNameJunior": null,
856
-          "SchoolFullName": "上海师范大学附属中学",
857
-          "pdf_total": "749",
858
-          "stored_score_total": "699",
859
-          "page": 2
860
-        },
861
-        {
862
-          "ID": 32303,
863
-          "SchoolFullNameJunior": null,
864
-          "SchoolFullName": "上海市川沙中学",
865
-          "pdf_total": "737",
866
-          "stored_score_total": "687",
840
+          "SchoolFullName": "上海市高桥中学",
841
+          "pdf_total": "724",
842
+          "stored_score_total": "674",
867 843
           "page": 2
868 844
         },
869 845
         {
@@ -881,6 +857,30 @@
881 857
           "pdf_total": "767.5",
882 858
           "stored_score_total": "717.5",
883 859
           "page": 1
860
+        },
861
+        {
862
+          "ID": 32293,
863
+          "SchoolFullNameJunior": null,
864
+          "SchoolFullName": "上海师范大学附属中学宝山分校",
865
+          "pdf_total": "738",
866
+          "stored_score_total": "688",
867
+          "page": 2
868
+        },
869
+        {
870
+          "ID": 32316,
871
+          "SchoolFullNameJunior": null,
872
+          "SchoolFullName": "上海市崇明中学",
873
+          "pdf_total": "707",
874
+          "stored_score_total": "657",
875
+          "page": 3
876
+        },
877
+        {
878
+          "ID": 32307,
879
+          "SchoolFullNameJunior": null,
880
+          "SchoolFullName": "华东师范大学第三附属中学",
881
+          "pdf_total": "713.5",
882
+          "stored_score_total": "663.5",
883
+          "page": 2
884 884
         }
885 885
       ]
886 886
     },
@@ -1182,15 +1182,15 @@
1182 1182
       "district_id": 7,
1183 1183
       "district": "杨浦区",
1184 1184
       "score_type": "名额到区",
1185
-      "pdf_parsed_rows": 66,
1185
+      "pdf_parsed_rows": 67,
1186 1186
       "db_rows": 67,
1187
-      "auditable_imported_rows": 66,
1187
+      "auditable_imported_rows": 67,
1188 1188
       "sample_rate": "0.30",
1189
-      "sample_size": 20,
1189
+      "sample_size": 21,
1190 1190
       "sample_errors": [],
1191 1191
       "full_recheck_triggered": false,
1192 1192
       "full_recheck_errors": [],
1193
-      "not_audited_pending_rows": 1,
1193
+      "not_audited_pending_rows": 0,
1194 1194
       "pending_match_problems": 0,
1195 1195
       "pending_duplicate_target_rows": 0,
1196 1196
       "sample_preview": [
@@ -1203,29 +1203,21 @@
1203 1203
           "page": 1
1204 1204
         },
1205 1205
         {
1206
-          "ID": 32360,
1206
+          "ID": 32359,
1207 1207
           "SchoolFullNameJunior": null,
1208
-          "SchoolFullName": "上海市嘉定区第一中学",
1209
-          "pdf_total": "734.5",
1210
-          "stored_score_total": "684.5",
1208
+          "SchoolFullName": "华东师范大学第二附属中学(宝山校区)",
1209
+          "pdf_total": "763",
1210
+          "stored_score_total": "713",
1211 1211
           "page": 2
1212 1212
         },
1213 1213
         {
1214
-          "ID": 32358,
1214
+          "ID": 32356,
1215 1215
           "SchoolFullNameJunior": null,
1216
-          "SchoolFullName": "上海师范大学附属中学宝山分校",
1217
-          "pdf_total": "735.5",
1218
-          "stored_score_total": "685.5",
1216
+          "SchoolFullName": "上海大学附属中学",
1217
+          "pdf_total": "732",
1218
+          "stored_score_total": "682",
1219 1219
           "page": 2
1220 1220
         },
1221
-        {
1222
-          "ID": 32377,
1223
-          "SchoolFullNameJunior": null,
1224
-          "SchoolFullName": "上海市青浦高级中学",
1225
-          "pdf_total": "707",
1226
-          "stored_score_total": "657",
1227
-          "page": 3
1228
-        },
1229 1221
         {
1230 1222
           "ID": 32321,
1231 1223
           "SchoolFullNameJunior": null,
@@ -1235,19 +1227,19 @@
1235 1227
           "page": 1
1236 1228
         },
1237 1229
         {
1238
-          "ID": 32362,
1230
+          "ID": 32361,
1239 1231
           "SchoolFullNameJunior": null,
1240
-          "SchoolFullName": "上海师范大学附属中学嘉定新城分校",
1241
-          "pdf_total": "708.5",
1242
-          "stored_score_total": "658.5",
1232
+          "SchoolFullName": "上海交通大学附属中学嘉定分校",
1233
+          "pdf_total": "765",
1234
+          "stored_score_total": "715",
1243 1235
           "page": 2
1244 1236
         },
1245 1237
         {
1246
-          "ID": 32366,
1238
+          "ID": 32370,
1247 1239
           "SchoolFullNameJunior": null,
1248
-          "SchoolFullName": "上海市洋泾中学",
1249
-          "pdf_total": "729",
1250
-          "stored_score_total": "679",
1240
+          "SchoolFullName": "上海市浦东复旦附中分校",
1241
+          "pdf_total": "758.5",
1242
+          "stored_score_total": "708.5",
1251 1243
           "page": 2
1252 1244
         },
1253 1245
         {
@@ -1259,19 +1251,27 @@
1259 1251
           "page": 1
1260 1252
         },
1261 1253
         {
1262
-          "ID": 32354,
1254
+          "ID": 32353,
1263 1255
           "SchoolFullNameJunior": null,
1264
-          "SchoolFullName": "上海交通大学附属中学闵行分校",
1265
-          "pdf_total": "765",
1266
-          "stored_score_total": "715",
1256
+          "SchoolFullName": "上海师范大学附属中学闵行分校",
1257
+          "pdf_total": "739.5",
1258
+          "stored_score_total": "689.5",
1267 1259
           "page": 2
1268 1260
         },
1269 1261
         {
1270
-          "ID": 32369,
1262
+          "ID": 32362,
1271 1263
           "SchoolFullNameJunior": null,
1272
-          "SchoolFullName": "上海中学东校",
1273
-          "pdf_total": "752.5",
1274
-          "stored_score_total": "702.5",
1264
+          "SchoolFullName": "上海师范大学附属中学嘉定新城分校",
1265
+          "pdf_total": "708.5",
1266
+          "stored_score_total": "658.5",
1267
+          "page": 2
1268
+        },
1269
+        {
1270
+          "ID": 32351,
1271
+          "SchoolFullNameJunior": null,
1272
+          "SchoolFullName": "上海市闵行中学",
1273
+          "pdf_total": "744.5",
1274
+          "stored_score_total": "694.5",
1275 1275
           "page": 2
1276 1276
         }
1277 1277
       ]
@@ -1280,50 +1280,58 @@
1280 1280
       "district_id": 7,
1281 1281
       "district": "杨浦区",
1282 1282
       "score_type": "名额到校",
1283
-      "pdf_parsed_rows": 140,
1283
+      "pdf_parsed_rows": 141,
1284 1284
       "db_rows": 144,
1285
-      "auditable_imported_rows": 140,
1285
+      "auditable_imported_rows": 141,
1286 1286
       "sample_rate": "0.30",
1287
-      "sample_size": 42,
1287
+      "sample_size": 43,
1288 1288
       "sample_errors": [],
1289 1289
       "full_recheck_triggered": false,
1290 1290
       "full_recheck_errors": [],
1291
-      "not_audited_pending_rows": 4,
1291
+      "not_audited_pending_rows": 3,
1292 1292
       "pending_match_problems": 0,
1293 1293
       "pending_duplicate_target_rows": 0,
1294 1294
       "sample_preview": [
1295 1295
         {
1296
-          "ID": 33704,
1297
-          "SchoolFullNameJunior": "上海市二十五中学",
1298
-          "SchoolFullName": "上海市上海中学",
1299
-          "pdf_total": "751",
1300
-          "stored_score_total": "701",
1296
+          "ID": 33700,
1297
+          "SchoolFullNameJunior": "上海市东辽阳中学",
1298
+          "SchoolFullName": "同济大学第一附属中学",
1299
+          "pdf_total": "704",
1300
+          "stored_score_total": "654",
1301 1301
           "page": 2
1302 1302
         },
1303 1303
         {
1304
-          "ID": 33698,
1304
+          "ID": 33699,
1305 1305
           "SchoolFullNameJunior": "上海市东辽阳中学",
1306
-          "SchoolFullName": "上海市控江中学",
1307
-          "pdf_total": "757",
1308
-          "stored_score_total": "707",
1306
+          "SchoolFullName": "上海市杨浦高级中学",
1307
+          "pdf_total": "699.5",
1308
+          "stored_score_total": "649.5",
1309 1309
           "page": 2
1310 1310
         },
1311 1311
         {
1312
-          "ID": 33751,
1312
+          "ID": 33752,
1313 1313
           "SchoolFullNameJunior": "上海民办杨浦凯慧初级中学",
1314
-          "SchoolFullName": "上海市控江中学",
1315
-          "pdf_total": "759",
1316
-          "stored_score_total": "709",
1314
+          "SchoolFullName": "上海市杨浦高级中学",
1315
+          "pdf_total": "737.5",
1316
+          "stored_score_total": "687.5",
1317 1317
           "page": 3
1318 1318
         },
1319 1319
         {
1320
-          "ID": 33691,
1320
+          "ID": 33692,
1321 1321
           "SchoolFullNameJunior": "上海市新大桥中学",
1322
-          "SchoolFullName": "上海市控江中学",
1323
-          "pdf_total": "751",
1324
-          "stored_score_total": "701",
1322
+          "SchoolFullName": "上海市杨浦高级中学",
1323
+          "pdf_total": "730.5",
1324
+          "stored_score_total": "680.5",
1325 1325
           "page": 1
1326 1326
         },
1327
+        {
1328
+          "ID": 33806,
1329
+          "SchoolFullNameJunior": "复旦大学第二附属学校",
1330
+          "SchoolFullName": "上海市控江中学",
1331
+          "pdf_total": "745",
1332
+          "stored_score_total": "695",
1333
+          "page": 5
1334
+        },
1327 1335
         {
1328 1336
           "ID": 33809,
1329 1337
           "SchoolFullNameJunior": "复旦大学第二附属学校",
@@ -1333,43 +1341,35 @@
1333 1341
           "page": 5
1334 1342
         },
1335 1343
         {
1336
-          "ID": 33750,
1344
+          "ID": 33747,
1337 1345
           "SchoolFullNameJunior": "上海市存志学校",
1338
-          "SchoolFullName": "上海交通大学附属中学",
1339
-          "pdf_total": "766.5",
1340
-          "stored_score_total": "716.5",
1346
+          "SchoolFullName": "上海市控江中学",
1347
+          "pdf_total": "757",
1348
+          "stored_score_total": "707",
1341 1349
           "page": 3
1342 1350
         },
1343 1351
         {
1344
-          "ID": 33724,
1352
+          "ID": 33725,
1345 1353
           "SchoolFullNameJunior": "上海市思源中学",
1346
-          "SchoolFullName": "上海市控江中学",
1347
-          "pdf_total": "752",
1348
-          "stored_score_total": "702",
1354
+          "SchoolFullName": "上海市杨浦高级中学",
1355
+          "pdf_total": "740",
1356
+          "stored_score_total": "690",
1349 1357
           "page": 2
1350 1358
         },
1351 1359
         {
1352
-          "ID": 33726,
1360
+          "ID": 33724,
1353 1361
           "SchoolFullNameJunior": "上海市思源中学",
1354
-          "SchoolFullName": "同济大学第一附属中学",
1355
-          "pdf_total": "733",
1356
-          "stored_score_total": "683",
1362
+          "SchoolFullName": "上海市控江中学",
1363
+          "pdf_total": "752",
1364
+          "stored_score_total": "702",
1357 1365
           "page": 2
1358 1366
         },
1359 1367
         {
1360
-          "ID": 33786,
1368
+          "ID": 33783,
1361 1369
           "SchoolFullNameJunior": "上海市昆明学校",
1362
-          "SchoolFullName": "复旦大学附属中学",
1363
-          "pdf_total": "758.5",
1364
-          "stored_score_total": "708.5",
1365
-          "page": 4
1366
-        },
1367
-        {
1368
-          "ID": 33765,
1369
-          "SchoolFullNameJunior": "上海市长阳实验学校",
1370 1370
           "SchoolFullName": "上海市控江中学",
1371
-          "pdf_total": "748",
1372
-          "stored_score_total": "698",
1371
+          "pdf_total": "748.5",
1372
+          "stored_score_total": "698.5",
1373 1373
           "page": 4
1374 1374
         }
1375 1375
       ]
@@ -1476,15 +1476,15 @@
1476 1476
       "district_id": 8,
1477 1477
       "district": "闵行区",
1478 1478
       "score_type": "名额到校",
1479
-      "pdf_parsed_rows": 437,
1479
+      "pdf_parsed_rows": 438,
1480 1480
       "db_rows": 460,
1481
-      "auditable_imported_rows": 437,
1481
+      "auditable_imported_rows": 438,
1482 1482
       "sample_rate": "0.30",
1483 1483
       "sample_size": 132,
1484 1484
       "sample_errors": [],
1485 1485
       "full_recheck_triggered": false,
1486 1486
       "full_recheck_errors": [],
1487
-      "not_audited_pending_rows": 23,
1487
+      "not_audited_pending_rows": 22,
1488 1488
       "pending_match_problems": 0,
1489 1489
       "pending_duplicate_target_rows": 0,
1490 1490
       "sample_preview": [
@@ -1497,11 +1497,11 @@
1497 1497
           "page": 6
1498 1498
         },
1499 1499
         {
1500
-          "ID": 34178,
1501
-          "SchoolFullNameJunior": "上海市闵行区七宝文来学校",
1502
-          "SchoolFullName": "上海市第二中学(梅陇校区)",
1503
-          "pdf_total": "737",
1504
-          "stored_score_total": "687",
1500
+          "ID": 34167,
1501
+          "SchoolFullNameJunior": "上海交通大学附属闵行马桥实验学校",
1502
+          "SchoolFullName": "上海市闵行中学",
1503
+          "pdf_total": "732.5",
1504
+          "stored_score_total": "682.5",
1505 1505
           "page": 13
1506 1506
         },
1507 1507
         {
@@ -1529,27 +1529,27 @@
1529 1529
           "page": 15
1530 1530
         },
1531 1531
         {
1532
-          "ID": 34175,
1532
+          "ID": 34173,
1533 1533
           "SchoolFullNameJunior": "上海市闵行区七宝文来学校",
1534
-          "SchoolFullName": "华东师范大学第二附属中学闵行紫竹分校",
1535
-          "pdf_total": "749",
1536
-          "stored_score_total": "699",
1534
+          "SchoolFullName": "上海市七宝中学",
1535
+          "pdf_total": "761.5",
1536
+          "stored_score_total": "711.5",
1537 1537
           "page": 13
1538 1538
         },
1539 1539
         {
1540
-          "ID": 34146,
1540
+          "ID": 34152,
1541 1541
           "SchoolFullNameJunior": "上海市闵行区君莲学校",
1542
-          "SchoolFullName": "上海市七宝中学",
1543
-          "pdf_total": "754.5",
1544
-          "stored_score_total": "704.5",
1542
+          "SchoolFullName": "上海交通大学附属中学",
1543
+          "pdf_total": "759",
1544
+          "stored_score_total": "709",
1545 1545
           "page": 12
1546 1546
         },
1547 1547
         {
1548
-          "ID": 34209,
1548
+          "ID": 34207,
1549 1549
           "SchoolFullNameJunior": "上海市民办协和双语尚音学校",
1550
-          "SchoolFullName": "华东师范大学第二附属中学闵行紫竹分校",
1551
-          "pdf_total": "733.5",
1552
-          "stored_score_total": "683.5",
1550
+          "SchoolFullName": "上海市七宝中学",
1551
+          "pdf_total": "754.5",
1552
+          "stored_score_total": "704.5",
1553 1553
           "page": 14
1554 1554
         },
1555 1555
         {
@@ -1561,11 +1561,11 @@
1561 1561
           "page": 5
1562 1562
         },
1563 1563
         {
1564
-          "ID": 34060,
1565
-          "SchoolFullNameJunior": "上海市文来中学",
1566
-          "SchoolFullName": "上海市向明中学(浦江校区)",
1567
-          "pdf_total": "740",
1568
-          "stored_score_total": "690",
1564
+          "ID": 34048,
1565
+          "SchoolFullNameJunior": "上海市七宝中学附属鑫都实验中学",
1566
+          "SchoolFullName": "上海市闵行中学",
1567
+          "pdf_total": "739",
1568
+          "stored_score_total": "689",
1569 1569
           "page": 9
1570 1570
         }
1571 1571
       ]
@@ -1672,40 +1672,40 @@
1672 1672
       "district_id": 9,
1673 1673
       "district": "宝山区",
1674 1674
       "score_type": "名额到校",
1675
-      "pdf_parsed_rows": 347,
1675
+      "pdf_parsed_rows": 348,
1676 1676
       "db_rows": 343,
1677
-      "auditable_imported_rows": 322,
1677
+      "auditable_imported_rows": 323,
1678 1678
       "sample_rate": "0.30",
1679 1679
       "sample_size": 97,
1680 1680
       "sample_errors": [],
1681 1681
       "full_recheck_triggered": false,
1682 1682
       "full_recheck_errors": [],
1683
-      "not_audited_pending_rows": 21,
1683
+      "not_audited_pending_rows": 20,
1684 1684
       "pending_match_problems": 0,
1685 1685
       "pending_duplicate_target_rows": 10,
1686 1686
       "sample_preview": [
1687 1687
         {
1688
-          "ID": 37240,
1689
-          "SchoolFullNameJunior": "上海市宝山区淞谊实验学校",
1690
-          "SchoolFullName": "上海市行知中学",
1691
-          "pdf_total": "733",
1692
-          "stored_score_total": "683",
1688
+          "ID": 37237,
1689
+          "SchoolFullNameJunior": "上海世外教育附属宝山大华实验学校",
1690
+          "SchoolFullName": "华东师范大学第二附属中学(宝山校区)",
1691
+          "pdf_total": "757.5",
1692
+          "stored_score_total": "707.5",
1693 1693
           "page": 8
1694 1694
         },
1695 1695
         {
1696
-          "ID": 37147,
1696
+          "ID": 37145,
1697 1697
           "SchoolFullNameJunior": "上海市顾村中学",
1698
-          "SchoolFullName": "上海大学附属中学",
1699
-          "pdf_total": "719.5",
1700
-          "stored_score_total": "669.5",
1698
+          "SchoolFullName": "上海市行知中学",
1699
+          "pdf_total": "728.5",
1700
+          "stored_score_total": "678.5",
1701 1701
           "page": 5
1702 1702
         },
1703 1703
         {
1704
-          "ID": 37024,
1704
+          "ID": 37022,
1705 1705
           "SchoolFullNameJunior": "上海市吴淞初级中学",
1706
-          "SchoolFullName": "上海师范大学附属中学宝山分校",
1707
-          "pdf_total": "737",
1708
-          "stored_score_total": "687",
1706
+          "SchoolFullName": "上海大学附属中学",
1707
+          "pdf_total": "719.5",
1708
+          "stored_score_total": "669.5",
1709 1709
           "page": 1
1710 1710
         },
1711 1711
         {
@@ -1717,51 +1717,51 @@
1717 1717
           "page": 1
1718 1718
         },
1719 1719
         {
1720
-          "ID": 37328,
1720
+          "ID": 37326,
1721 1721
           "SchoolFullNameJunior": "上海世外教育附属宝山美兰湖实验学校",
1722
-          "SchoolFullName": "上海大学附属中学",
1723
-          "pdf_total": "731.5",
1724
-          "stored_score_total": "681.5",
1722
+          "SchoolFullName": "上海市行知中学",
1723
+          "pdf_total": "732",
1724
+          "stored_score_total": "682",
1725 1725
           "page": 11
1726 1726
         },
1727 1727
         {
1728
-          "ID": 37203,
1728
+          "ID": 37206,
1729 1729
           "SchoolFullNameJunior": "上海市宝山区上海大学附属中学实验学校",
1730
-          "SchoolFullName": "上海市吴淞中学",
1731
-          "pdf_total": "722.5",
1732
-          "stored_score_total": "672.5",
1730
+          "SchoolFullName": "上海师范大学附属中学宝山分校",
1731
+          "pdf_total": "740",
1732
+          "stored_score_total": "690",
1733 1733
           "page": 7
1734 1734
         },
1735 1735
         {
1736
-          "ID": 37219,
1737
-          "SchoolFullNameJunior": "上海大学附属学校",
1738
-          "SchoolFullName": "上海市行知中学",
1739
-          "pdf_total": "727.5",
1740
-          "stored_score_total": "677.5",
1736
+          "ID": 37216,
1737
+          "SchoolFullNameJunior": "华东师范大学宝山实验学校",
1738
+          "SchoolFullName": "华东师范大学第二附属中学(宝山校区)",
1739
+          "pdf_total": "756",
1740
+          "stored_score_total": "706",
1741 1741
           "page": 8
1742 1742
         },
1743 1743
         {
1744
-          "ID": 37305,
1745
-          "SchoolFullNameJunior": "上海民办至德实验学校",
1746
-          "SchoolFullName": "上海市行知中学",
1747
-          "pdf_total": "743.5",
1748
-          "stored_score_total": "693.5",
1744
+          "ID": 37302,
1745
+          "SchoolFullNameJunior": "上海市行知中学附属宝山实验学校",
1746
+          "SchoolFullName": "华东师范大学第二附属中学(宝山校区)",
1747
+          "pdf_total": "740",
1748
+          "stored_score_total": "690",
1749 1749
           "page": 10
1750 1750
         },
1751 1751
         {
1752
-          "ID": 37197,
1753
-          "SchoolFullNameJunior": "上海市宝山区天馨学校",
1754
-          "SchoolFullName": "上海市行知中学",
1755
-          "pdf_total": "738.5",
1756
-          "stored_score_total": "688.5",
1752
+          "ID": 37195,
1753
+          "SchoolFullNameJunior": "上海市吴淞实验学校",
1754
+          "SchoolFullName": "华东师范大学第二附属中学(宝山校区)",
1755
+          "pdf_total": "755.5",
1756
+          "stored_score_total": "705.5",
1757 1757
           "page": 7
1758 1758
         },
1759 1759
         {
1760
-          "ID": 37198,
1760
+          "ID": 37201,
1761 1761
           "SchoolFullNameJunior": "上海市宝山区天馨学校",
1762
-          "SchoolFullName": "上海市吴淞中学",
1763
-          "pdf_total": "713",
1764
-          "stored_score_total": "663",
1762
+          "SchoolFullName": "上海师范大学附属中学宝山分校",
1763
+          "pdf_total": "722.5",
1764
+          "stored_score_total": "672.5",
1765 1765
           "page": 7
1766 1766
         }
1767 1767
       ]
@@ -2456,96 +2456,96 @@
2456 2456
       "district_id": 13,
2457 2457
       "district": "松江区",
2458 2458
       "score_type": "名额到校",
2459
-      "pdf_parsed_rows": 201,
2459
+      "pdf_parsed_rows": 202,
2460 2460
       "db_rows": 190,
2461
-      "auditable_imported_rows": 176,
2461
+      "auditable_imported_rows": 177,
2462 2462
       "sample_rate": "0.30",
2463
-      "sample_size": 53,
2463
+      "sample_size": 54,
2464 2464
       "sample_errors": [],
2465 2465
       "full_recheck_triggered": false,
2466 2466
       "full_recheck_errors": [],
2467
-      "not_audited_pending_rows": 14,
2467
+      "not_audited_pending_rows": 13,
2468 2468
       "pending_match_problems": 19,
2469 2469
       "pending_duplicate_target_rows": 3,
2470 2470
       "sample_preview": [
2471 2471
         {
2472
-          "ID": 38698,
2473
-          "SchoolFullNameJunior": "上海市松江区佘山学校",
2474
-          "SchoolFullName": "上海市松江二中",
2475
-          "pdf_total": "713",
2476
-          "stored_score_total": "663",
2472
+          "ID": 38695,
2473
+          "SchoolFullNameJunior": "上海市松江区仓桥学校",
2474
+          "SchoolFullName": "上海外国语大学附属外国语学校松江云间中学",
2475
+          "pdf_total": "729",
2476
+          "stored_score_total": "679",
2477 2477
           "page": 4
2478 2478
         },
2479 2479
         {
2480
-          "ID": 38742,
2480
+          "ID": 38741,
2481 2481
           "SchoolFullNameJunior": "上海外国语大学松江外国语学校",
2482
-          "SchoolFullName": "上海师范大学附属中学松江分校",
2483
-          "pdf_total": "738",
2484
-          "stored_score_total": "688",
2482
+          "SchoolFullName": "华东师范大学第二附属中学松江分校",
2483
+          "pdf_total": "760",
2484
+          "stored_score_total": "710",
2485 2485
           "page": 5
2486 2486
         },
2487 2487
         {
2488
-          "ID": 38776,
2488
+          "ID": 38773,
2489 2489
           "SchoolFullNameJunior": "上海工程技术大学附属松江泗泾实验学校",
2490
-          "SchoolFullName": "华东师范大学第二附属中学松江分校",
2491
-          "pdf_total": "760.5",
2492
-          "stored_score_total": "710.5",
2490
+          "SchoolFullName": "上海市松江二中",
2491
+          "pdf_total": "751",
2492
+          "stored_score_total": "701",
2493 2493
           "page": 6
2494 2494
         },
2495 2495
         {
2496
-          "ID": 38779,
2496
+          "ID": 38782,
2497 2497
           "SchoolFullNameJunior": "上海市三新学校松江思贤分校",
2498
-          "SchoolFullName": "上海市松江一中",
2499
-          "pdf_total": "733",
2500
-          "stored_score_total": "683",
2498
+          "SchoolFullName": "上海师范大学附属中学松江分校",
2499
+          "pdf_total": "737.5",
2500
+          "stored_score_total": "687.5",
2501 2501
           "page": 7
2502 2502
         },
2503 2503
         {
2504
-          "ID": 38755,
2504
+          "ID": 38754,
2505 2505
           "SchoolFullNameJunior": "华东政法大学附属松江实验学校",
2506
-          "SchoolFullName": "上海外国语大学附属外国语学校松江云间中学",
2507
-          "pdf_total": "747",
2508
-          "stored_score_total": "697",
2506
+          "SchoolFullName": "上海市松江一中",
2507
+          "pdf_total": "734",
2508
+          "stored_score_total": "684",
2509 2509
           "page": 6
2510 2510
         },
2511 2511
         {
2512
-          "ID": 38667,
2512
+          "ID": 38666,
2513 2513
           "SchoolFullNameJunior": "上海市松江区洞泾学校",
2514
-          "SchoolFullName": "上海师范大学附属中学松江分校",
2515
-          "pdf_total": "734",
2516
-          "stored_score_total": "684",
2514
+          "SchoolFullName": "华东师范大学第二附属中学松江分校",
2515
+          "pdf_total": "742",
2516
+          "stored_score_total": "692",
2517 2517
           "page": 2
2518 2518
         },
2519 2519
         {
2520
-          "ID": 38778,
2521
-          "SchoolFullNameJunior": "上海市三新学校松江思贤分校",
2522
-          "SchoolFullName": "上海市松江二中",
2523
-          "pdf_total": "750.5",
2524
-          "stored_score_total": "700.5",
2520
+          "ID": 38775,
2521
+          "SchoolFullNameJunior": "上海工程技术大学附属松江泗泾实验学校",
2522
+          "SchoolFullName": "上海外国语大学附属外国语学校松江云间中学",
2523
+          "pdf_total": "749",
2524
+          "stored_score_total": "699",
2525 2525
           "page": 6
2526 2526
         },
2527 2527
         {
2528
-          "ID": 38744,
2528
+          "ID": 38747,
2529 2529
           "SchoolFullNameJunior": "东华大学附属实验学校",
2530
-          "SchoolFullName": "上海市松江一中",
2531
-          "pdf_total": "735",
2532
-          "stored_score_total": "685",
2530
+          "SchoolFullName": "上海师范大学附属中学松江分校",
2531
+          "pdf_total": "744.5",
2532
+          "stored_score_total": "694.5",
2533 2533
           "page": 5
2534 2534
         },
2535 2535
         {
2536
-          "ID": 38676,
2536
+          "ID": 38673,
2537 2537
           "SchoolFullNameJunior": "上海市松江区民乐学校",
2538
-          "SchoolFullName": "华东师范大学第二附属中学松江分校",
2539
-          "pdf_total": "758",
2540
-          "stored_score_total": "708",
2538
+          "SchoolFullName": "上海市松江二中",
2539
+          "pdf_total": "749",
2540
+          "stored_score_total": "699",
2541 2541
           "page": 3
2542 2542
         },
2543 2543
         {
2544
-          "ID": 38657,
2544
+          "ID": 38656,
2545 2545
           "SchoolFullNameJunior": "上海市松江区民办茸一中学",
2546
-          "SchoolFullName": "上海师范大学附属中学松江分校",
2547
-          "pdf_total": "735",
2548
-          "stored_score_total": "685",
2546
+          "SchoolFullName": "华东师范大学第二附属中学松江分校",
2547
+          "pdf_total": "746",
2548
+          "stored_score_total": "696",
2549 2549
           "page": 2
2550 2550
         }
2551 2551
       ]
@@ -2654,62 +2654,62 @@
2654 2654
       "score_type": "名额到校",
2655 2655
       "pdf_parsed_rows": 98,
2656 2656
       "db_rows": 99,
2657
-      "auditable_imported_rows": 92,
2657
+      "auditable_imported_rows": 98,
2658 2658
       "sample_rate": "0.30",
2659
-      "sample_size": 28,
2659
+      "sample_size": 30,
2660 2660
       "sample_errors": [],
2661 2661
       "full_recheck_triggered": false,
2662 2662
       "full_recheck_errors": [],
2663
-      "not_audited_pending_rows": 7,
2663
+      "not_audited_pending_rows": 1,
2664 2664
       "pending_match_problems": 0,
2665
-      "pending_duplicate_target_rows": 3,
2665
+      "pending_duplicate_target_rows": 0,
2666 2666
       "sample_preview": [
2667 2667
         {
2668
-          "ID": 39183,
2669
-          "SchoolFullNameJunior": "上海市青浦区思源中学",
2670
-          "SchoolFullName": "上海市青浦高级中学",
2671
-          "pdf_total": "723.5",
2672
-          "stored_score_total": "673.5",
2668
+          "ID": 39151,
2669
+          "SchoolFullNameJunior": "上海师范大学附属青浦实验中学",
2670
+          "SchoolFullName": "复旦大学附属中学青浦分校",
2671
+          "pdf_total": "735",
2672
+          "stored_score_total": "685",
2673 2673
           "page": 2
2674 2674
         },
2675 2675
         {
2676
-          "ID": 39210,
2677
-          "SchoolFullNameJunior": "上海政法学院附属青浦崧淀中学",
2676
+          "ID": 39209,
2677
+          "SchoolFullNameJunior": "上海市青浦区金泽中学",
2678 2678
           "SchoolFullName": "上海市朱家角中学",
2679
-          "pdf_total": "703.5",
2680
-          "stored_score_total": "653.5",
2679
+          "pdf_total": "695.5",
2680
+          "stored_score_total": "645.5",
2681 2681
           "page": 2
2682 2682
         },
2683 2683
         {
2684
-          "ID": 39161,
2685
-          "SchoolFullNameJunior": "上海五浦汇实验学校",
2686
-          "SchoolFullName": "复旦大学附属中学青浦分校",
2687
-          "pdf_total": "753.5",
2688
-          "stored_score_total": "703.5",
2684
+          "ID": 39221,
2685
+          "SchoolFullNameJunior": "上海宋庆龄学校",
2686
+          "SchoolFullName": "上海市朱家角中学",
2687
+          "pdf_total": "705.5",
2688
+          "stored_score_total": "655.5",
2689 2689
           "page": 3
2690 2690
         },
2691 2691
         {
2692
-          "ID": 39185,
2693
-          "SchoolFullNameJunior": "上海市毓华学校",
2692
+          "ID": 39183,
2693
+          "SchoolFullNameJunior": "上海市青浦区思源中学",
2694 2694
           "SchoolFullName": "上海市青浦高级中学",
2695
-          "pdf_total": "714.5",
2696
-          "stored_score_total": "664.5",
2697
-          "page": 3
2695
+          "pdf_total": "723.5",
2696
+          "stored_score_total": "673.5",
2697
+          "page": 2
2698 2698
         },
2699 2699
         {
2700
-          "ID": 39158,
2701
-          "SchoolFullNameJunior": "上海市毓秀学校",
2700
+          "ID": 39156,
2701
+          "SchoolFullNameJunior": "上海市博文学校",
2702 2702
           "SchoolFullName": "复旦大学附属中学青浦分校",
2703
-          "pdf_total": "752",
2704
-          "stored_score_total": "702",
2703
+          "pdf_total": "737",
2704
+          "stored_score_total": "687",
2705 2705
           "page": 3
2706 2706
         },
2707 2707
         {
2708
-          "ID": 39224,
2709
-          "SchoolFullNameJunior": "上海青浦区世外学校",
2710
-          "SchoolFullName": "上海市朱家角中学",
2711
-          "pdf_total": "688.5",
2712
-          "stored_score_total": "638.5",
2708
+          "ID": 39133,
2709
+          "SchoolFullNameJunior": "上海五浦汇实验学校",
2710
+          "SchoolFullName": "复旦大学附属中学",
2711
+          "pdf_total": "765",
2712
+          "stored_score_total": "715",
2713 2713
           "page": 3
2714 2714
         },
2715 2715
         {
@@ -2721,28 +2721,28 @@
2721 2721
           "page": 1
2722 2722
         },
2723 2723
         {
2724
-          "ID": 39192,
2725
-          "SchoolFullNameJunior": "上海五浦汇实验学校",
2726
-          "SchoolFullName": "上海市青浦高级中学",
2727
-          "pdf_total": "720.5",
2728
-          "stored_score_total": "670.5",
2724
+          "ID": 39159,
2725
+          "SchoolFullNameJunior": "上海宋庆龄学校",
2726
+          "SchoolFullName": "复旦大学附属中学青浦分校",
2727
+          "pdf_total": "762.5",
2728
+          "stored_score_total": "712.5",
2729 2729
           "page": 3
2730 2730
         },
2731 2731
         {
2732
-          "ID": 39191,
2733
-          "SchoolFullNameJunior": "上海市佳信学校",
2732
+          "ID": 39189,
2733
+          "SchoolFullNameJunior": "上海市毓秀学校",
2734 2734
           "SchoolFullName": "上海市青浦高级中学",
2735
-          "pdf_total": "701.5",
2736
-          "stored_score_total": "651.5",
2735
+          "pdf_total": "730.5",
2736
+          "stored_score_total": "680.5",
2737 2737
           "page": 3
2738 2738
         },
2739 2739
         {
2740
-          "ID": 39228,
2741
-          "SchoolFullNameJunior": "上海青浦兰生学校",
2740
+          "ID": 39223,
2741
+          "SchoolFullNameJunior": "上海五浦汇实验学校",
2742 2742
           "SchoolFullName": "上海市朱家角中学",
2743
-          "pdf_total": "703",
2744
-          "stored_score_total": "653",
2745
-          "page": 4
2743
+          "pdf_total": "704",
2744
+          "stored_score_total": "654",
2745
+          "page": 3
2746 2746
         }
2747 2747
       ]
2748 2748
     },
@@ -3180,8 +3180,8 @@
3180 3180
       {
3181 3181
         "DistrictID": 5,
3182 3182
         "db_rows": 66,
3183
-        "scored_rows": "65",
3184
-        "unscored_rows": "1",
3183
+        "scored_rows": "66",
3184
+        "unscored_rows": "0",
3185 3185
         "min_score": "641.50",
3186 3186
         "max_score": "719.50"
3187 3187
       },
@@ -3196,8 +3196,8 @@
3196 3196
       {
3197 3197
         "DistrictID": 7,
3198 3198
         "db_rows": 67,
3199
-        "scored_rows": "66",
3200
-        "unscored_rows": "1",
3199
+        "scored_rows": "67",
3200
+        "unscored_rows": "0",
3201 3201
         "min_score": "637.00",
3202 3202
         "max_score": "718.50"
3203 3203
       },
@@ -3326,24 +3326,24 @@
3326 3326
       {
3327 3327
         "DistrictID": 7,
3328 3328
         "db_rows": 144,
3329
-        "scored_rows": "140",
3330
-        "unscored_rows": "4",
3329
+        "scored_rows": "141",
3330
+        "unscored_rows": "3",
3331 3331
         "min_score": "649.50",
3332 3332
         "max_score": "718.00"
3333 3333
       },
3334 3334
       {
3335 3335
         "DistrictID": 8,
3336 3336
         "db_rows": 460,
3337
-        "scored_rows": "437",
3338
-        "unscored_rows": "23",
3337
+        "scored_rows": "438",
3338
+        "unscored_rows": "22",
3339 3339
         "min_score": "641.00",
3340 3340
         "max_score": "721.00"
3341 3341
       },
3342 3342
       {
3343 3343
         "DistrictID": 9,
3344 3344
         "db_rows": 343,
3345
-        "scored_rows": "322",
3346
-        "unscored_rows": "21",
3345
+        "scored_rows": "323",
3346
+        "unscored_rows": "20",
3347 3347
         "min_score": "617.50",
3348 3348
         "max_score": "715.00"
3349 3349
       },
@@ -3374,16 +3374,16 @@
3374 3374
       {
3375 3375
         "DistrictID": 13,
3376 3376
         "db_rows": 190,
3377
-        "scored_rows": "176",
3378
-        "unscored_rows": "14",
3377
+        "scored_rows": "177",
3378
+        "unscored_rows": "13",
3379 3379
         "min_score": "637.00",
3380 3380
         "max_score": "715.00"
3381 3381
       },
3382 3382
       {
3383 3383
         "DistrictID": 14,
3384 3384
         "db_rows": 99,
3385
-        "scored_rows": "92",
3386
-        "unscored_rows": "7",
3385
+        "scored_rows": "98",
3386
+        "unscored_rows": "1",
3387 3387
         "min_score": "619.00",
3388 3388
         "max_score": "717.50"
3389 3389
       },

+ 104 - 0
秒过分数线数据导入/mps_score_scores_2026_tie_preferred_backup.json

@@ -0,0 +1,104 @@
1
+[
2
+  {
3
+    "ID": 32299,
4
+    "ScoreYear": "2026",
5
+    "ScoreType": "名额到区",
6
+    "DistrictID": 5,
7
+    "SchoolOfGraduation": 0,
8
+    "SchoolFullNameJunior": null,
9
+    "SchoolTarget": "55",
10
+    "SchoolFullName": "上海市进才中学",
11
+    "PlanNum": 5,
12
+    "ScoreTotal": "700.50",
13
+    "Score1": "0.00",
14
+    "Score2": "0.00",
15
+    "Score3": "0.00",
16
+    "Score4": "0.00",
17
+    "ScoreTotalDifferenceValue": "0.00"
18
+  },
19
+  {
20
+    "ID": 32318,
21
+    "ScoreYear": "2026",
22
+    "ScoreType": "名额到区",
23
+    "DistrictID": 7,
24
+    "SchoolOfGraduation": 0,
25
+    "SchoolFullNameJunior": null,
26
+    "SchoolTarget": "2",
27
+    "SchoolFullName": "上海交通大学附属中学",
28
+    "PlanNum": 47,
29
+    "ScoreTotal": "0.00",
30
+    "Score1": "0.00",
31
+    "Score2": "0.00",
32
+    "Score3": "0.00",
33
+    "Score4": "0.00",
34
+    "ScoreTotalDifferenceValue": "0.00"
35
+  },
36
+  {
37
+    "ID": 33669,
38
+    "ScoreYear": "2026",
39
+    "ScoreType": "名额到校",
40
+    "DistrictID": 7,
41
+    "SchoolOfGraduation": 425,
42
+    "SchoolFullNameJunior": "上海市铁岭中学",
43
+    "SchoolTarget": "40",
44
+    "SchoolFullName": "上海市控江中学",
45
+    "PlanNum": 14,
46
+    "ScoreTotal": "0.00",
47
+    "Score1": "0.00",
48
+    "Score2": "0.00",
49
+    "Score3": "0.00",
50
+    "Score4": "0.00",
51
+    "ScoreTotalDifferenceValue": "0.00"
52
+  },
53
+  {
54
+    "ID": 34049,
55
+    "ScoreYear": "2026",
56
+    "ScoreType": "名额到校",
57
+    "DistrictID": 8,
58
+    "SchoolOfGraduation": 487,
59
+    "SchoolFullNameJunior": "上海市七宝中学附属鑫都实验中学",
60
+    "SchoolTarget": "44",
61
+    "SchoolFullName": "华东师范大学第二附属中学闵行紫竹分校",
62
+    "PlanNum": 7,
63
+    "ScoreTotal": "0.00",
64
+    "Score1": "0.00",
65
+    "Score2": "0.00",
66
+    "Score3": "0.00",
67
+    "Score4": "0.00",
68
+    "ScoreTotalDifferenceValue": "0.00"
69
+  },
70
+  {
71
+    "ID": 37011,
72
+    "ScoreYear": "2026",
73
+    "ScoreType": "名额到校",
74
+    "DistrictID": 9,
75
+    "SchoolOfGraduation": 500,
76
+    "SchoolFullNameJunior": "上海市淞谊中学",
77
+    "SchoolTarget": "49",
78
+    "SchoolFullName": "上海市吴淞中学",
79
+    "PlanNum": 7,
80
+    "ScoreTotal": "0.00",
81
+    "Score1": "0.00",
82
+    "Score2": "0.00",
83
+    "Score3": "0.00",
84
+    "Score4": "0.00",
85
+    "ScoreTotalDifferenceValue": "0.00"
86
+  },
87
+  {
88
+    "ID": 38643,
89
+    "ScoreYear": "2026",
90
+    "ScoreType": "名额到校",
91
+    "DistrictID": 13,
92
+    "SchoolOfGraduation": 1019,
93
+    "SchoolFullNameJunior": "上海外国语大学附属外国语学校松江云间中学",
94
+    "SchoolTarget": "63",
95
+    "SchoolFullName": "上海市松江二中",
96
+    "PlanNum": 6,
97
+    "ScoreTotal": "0.00",
98
+    "Score1": "0.00",
99
+    "Score2": "0.00",
100
+    "Score3": "0.00",
101
+    "Score4": "0.00",
102
+    "ScoreTotalDifferenceValue": "0.00"
103
+  }
104
+]