File size: 2,333 Bytes
53ea208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/env python3
"""Reconcile annotation files into one adjudicated row per key.

    uv run python human_labeling/merge.py [--out annotations/adjudicated.jsonl]

Reads every annotations/rulings*.jsonl + annotations/*-export.json
(export format: {"items": [...]}, same ruling objects), majority-votes
ties -> "REVIEW", and writes adjudicated.jsonl:
{"key", "verdict", "n_keep", "n_drop", "annotators", "origins"?, ...}
Kept inputs are never modified; rerun any time.
"""

import argparse
import glob
import json
from pathlib import Path

HERE = Path(__file__).resolve().parent
ANNOT = HERE / "annotations"


def load() -> list[dict]:
    rulings: list[dict] = []
    for f in sorted(glob.glob(str(ANNOT / "rulings*.jsonl"))):
        for line in open(f):
            line = line.strip()
            if line:
                rulings.append(json.loads(line))
    for f in sorted(glob.glob(str(ANNOT / "*-export.json"))):
        try:
            payload = json.load(open(f))
        except json.JSONDecodeError:
            continue
        items = payload.get("items", payload if isinstance(payload, list) else [])
        rulings.extend(i for i in items if isinstance(i, dict) and i.get("key"))
    return rulings


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--out", default=str(ANNOT / "adjudicated.jsonl"))
    a = ap.parse_args()
    by_key: dict[str, list[dict]] = {}
    for r in load():
        by_key.setdefault(r["key"], []).append(r)
    n_tie = 0
    with open(a.out, "w") as f:
        for key in sorted(by_key):
            rs = by_key[key]
            keep = sum(1 for r in rs if r["ruling"] == "DATA_MENTION")
            drop = sum(1 for r in rs if r["ruling"] == "NON_MENTION")
            verdict = "DATA_MENTION" if keep > drop else "NON_MENTION" if drop > keep else "REVIEW"
            n_tie += verdict == "REVIEW"
            f.write(json.dumps({
                "key": key, "verdict": verdict, "n_keep": keep, "n_drop": drop,
                "n": len(rs), "annotators": sorted({r.get("annotator", "?") for r in rs}),
                "surface": rs[0].get("surface"), "origin": rs[0].get("origin"),
                "head_score": rs[0].get("head_score"),
            }) + "\n")
    print(f"keys={len(by_key)} ties={n_tie} -> {a.out}")


if __name__ == "__main__":
    main()