v1.1.2: sandbox 卡卡片文本采集 + 解析为 performance 记录
sandbox 行为新发现:
1. 「续聘材料包」按钮不是下载文件,而是触发页面下方显示「续聘准备任务书」
+「单位清单」卡片(含 checklist + 近三年考核表)
2. 进入 hr/contracts 页面后需要刷新(page.reload)才能看到表格行
3. 表格列序扩展为 10 列:工号 / 姓名 / 单位 / 岗位 / 合同类型 / 起止日期 / 剩余天数 / 预警 / 建议 / 材料包按钮
4. contract_id 改用工号(稳定,不随姓名变化)
改动:
- platform_collector.collect_all:
* 进入 hr/contracts 后 page.reload 刷新触发 SPA mock 数据加载
* 新列序映射:r[0]=工号 r[1]=姓名 r[2]=单位 r[3]=岗位 r[4]=合同类型
r[5]=起止日期 r[6]=剩余天数 r[7]=预警 r[8]=建议
* 合同 end_date 从「2024-03-01 ~ 2026-04-15」截取 ~ 后的部分
* 点「续聘材料包」按钮 → 抓下方卡片 inner_text(含 checklist + 考核等次)
* 把卡片文本存入 materials_content(parse_method='sandbox_card')
- _persist_contracts 增加 sandbox_card 路径分支:写 materials_content 后同步调
repo.parse_sandbox_card_performance → 抽 year + 等次 → 写 performance_records
- repository.parse_sandbox_card_performance:双模式正则匹配
* 行格式 "2024\t称职"
* 叙述式 "2024年:等次「称职」"
- repository.upsert_performance_record + 等次 → 分数映射
(优秀=90 称职=75 基本称职=60 不称职=30)
实测(sandbox 3 合同):
- RPA-T20190033-20260415 钱实验 → 解析 4 条 performance
- RPA-T20200055-20260630 孙助理 → 解析 6 条
- RPA-T20180021-20260831 王明远 → 解析 6 条
- generate-renewal-task 全部输出 observe + decision_source=rules
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,82 @@ def list_performance_by_contract(
|
||||
).fetchall()
|
||||
|
||||
|
||||
# 等次 → 分数映射(v1.1.1 sandbox card 解析)
|
||||
_GRADE_TO_SCORE = {
|
||||
"优秀": 90.0,
|
||||
"称职": 75.0,
|
||||
"基本称职": 60.0,
|
||||
"不称职": 30.0,
|
||||
"不合格": 30.0,
|
||||
}
|
||||
|
||||
|
||||
def upsert_performance_record(
|
||||
conn: sqlite3.Connection,
|
||||
contract_id: str,
|
||||
year: int,
|
||||
grade: str,
|
||||
) -> bool:
|
||||
"""upsert 一条业绩记录(用 grade 映射分数)。
|
||||
|
||||
sandbox card 文本不含真分数时使用。返回是否实际写入。
|
||||
"""
|
||||
score = _GRADE_TO_SCORE.get(grade)
|
||||
if score is None:
|
||||
return False
|
||||
record_id = f"P-{contract_id}-{year}"
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO performance_records
|
||||
(record_id, contract_id, year, performance_score,
|
||||
moral_teaching_score, research_score, teaching_score)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(record_id) DO UPDATE SET
|
||||
performance_score = excluded.performance_score
|
||||
""",
|
||||
(record_id, contract_id, year, score, score, score, score),
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def parse_sandbox_card_performance(
|
||||
conn: sqlite3.Connection,
|
||||
contract_id: str,
|
||||
card_text: str,
|
||||
) -> int:
|
||||
"""从 sandbox 卡片文本抽 (year, 等次) → upsert 到 performance_records。
|
||||
|
||||
文本片段形如:
|
||||
year result
|
||||
2024 称职
|
||||
2025 优秀
|
||||
2024年:等次「称职」。
|
||||
返回写入条数。
|
||||
"""
|
||||
import re
|
||||
if not card_text:
|
||||
return 0
|
||||
|
||||
written = 0
|
||||
# 模式 1:行格式 "2024\t称职"
|
||||
for m in re.finditer(r"^(\d{4})\s*[ \t]+(优秀|称职|基本称职|不称职|不合格)\s*$",
|
||||
card_text, re.MULTILINE):
|
||||
year = int(m.group(1))
|
||||
grade = m.group(2)
|
||||
if upsert_performance_record(conn, contract_id, year, grade):
|
||||
written += 1
|
||||
|
||||
# 模式 2:叙述式 "2024年:等次「称职」"
|
||||
for m in re.finditer(r"(\d{4})\s*年[::]\s*等次[「\"']\s*(优秀|称职|基本称职|不称职|不合格)\s*[」\"']",
|
||||
card_text):
|
||||
year = int(m.group(1))
|
||||
grade = m.group(2)
|
||||
if upsert_performance_record(conn, contract_id, year, grade):
|
||||
written += 1
|
||||
|
||||
return written
|
||||
|
||||
|
||||
# ---------- alerts ----------
|
||||
|
||||
def insert_alert(
|
||||
|
||||
Reference in New Issue
Block a user