Spaces:
Running
Running
File size: 6,121 Bytes
21131b5 | 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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | #!/usr/bin/env python3
"""Build corpus_index.json + eval.json for the MuDABench viewer.
Reads the two source QA files (simple.json = concise final answers,
complex.json = longer analytical final answers) and derives:
- eval.json one entry per question (dataset-tagged); question, gold
final answer, supporting facts (source_answer) and the
supporting-document list (each with the value_* fields that
question used + their schema descriptions).
- corpus_index.json one entry per unique document (589 == the PDF corpus).
Title = "symbol 路 year 路 doctype"; value_* fields are the
union observed across every question that cites the doc;
`pdf` points at the HF dataset CDN (streamed, not bundled).
Run from the viewer repo root:
python scripts/build_data.py
Reads: simple.json, complex.json
Writes: eval.json, corpus_index.json
"""
import json
import os
from collections import OrderedDict
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SOURCES = [("simple", os.path.join(ROOT, "simple.json")),
("complex", os.path.join(ROOT, "complex.json"))]
EVAL_OUT = os.path.join(ROOT, "eval.json")
CORPUS_OUT = os.path.join(ROOT, "corpus_index.json")
# PDFs live on the Hugging Face dataset; the viewer streams them from the CDN
# so the Space never has to bundle the ~4 GB corpus.
PDF_URL = "https://huggingface.co/datasets/Zhanli-Li/MuDABench/resolve/main/data/pdf/{id}.pdf"
def value_fields(meta):
"""Return [(field_name, value), ...] for the value_* keys in a metadata row."""
out = []
for k, v in meta.items():
if k.startswith("value_"):
out.append((k, v))
return out
def doc_title(symbol, year, doctype):
parts = [str(p) for p in (symbol, year, doctype) if p not in (None, "")]
return " 路 ".join(parts) if parts else "(untitled)"
def main():
eval_rows = []
# corpus[id] accumulates the merged view of a document across all questions.
corpus = OrderedDict()
for dataset, path in SOURCES:
with open(path, encoding="utf-8") as f:
data = json.load(f)
for q in data:
qid = q.get("question_id")
src = q.get("source_answer")
if isinstance(src, str):
src = [src] if src.strip() else []
elif not isinstance(src, list):
src = []
docs = []
for meta in q.get("metadata", []):
did = meta.get("id")
symbol = meta.get("symbol")
year = meta.get("year")
doctype = meta.get("doctype")
schema = meta.get("schema", {}) or {}
vfields = value_fields(meta)
docs.append({
"id": did,
"title": doc_title(symbol, year, doctype),
"symbol": symbol,
"year": year,
"doctype": doctype,
"fields": [
{"name": n, "value": v, "desc": schema.get(n, "")}
for n, v in vfields
],
})
if did is None:
continue
c = corpus.get(did)
if c is None:
c = corpus[did] = {
"id": did,
"symbol": symbol,
"year": year,
"doctype": doctype,
"title": doc_title(symbol, year, doctype),
"_fields": OrderedDict(), # name -> {"desc", "values"[]}
"referenced_by": [],
}
for n, v in vfields:
slot = c["_fields"].get(n)
if slot is None:
slot = c["_fields"][n] = {"desc": schema.get(n, ""), "values": []}
if not slot["desc"] and schema.get(n):
slot["desc"] = schema[n]
if v not in slot["values"]:
slot["values"].append(v)
c["referenced_by"].append({"qid": qid, "dataset": dataset})
eval_rows.append({
"qid": qid,
"dataset": dataset,
"question": q.get("question", ""),
"final_answer": q.get("final_answer", ""),
"source_answer": src,
"docs": docs,
})
# finalize corpus entries
corpus_rows = []
for c in corpus.values():
fields = [
{"name": n, "desc": slot["desc"], "values": slot["values"]}
for n, slot in c["_fields"].items()
]
corpus_rows.append({
"id": c["id"],
"title": c["title"],
"symbol": c["symbol"],
"year": c["year"],
"doctype": c["doctype"],
"fields": fields,
"n_refs": len(c["referenced_by"]),
"referenced_by": c["referenced_by"],
"pdf": PDF_URL.format(id=c["id"]),
})
corpus_rows.sort(key=lambda d: (
str(d["doctype"] or ""), str(d["symbol"] or ""), str(d["year"] or "")
))
# collision report (docs sharing an identical title)
seen = {}
collisions = 0
for d in corpus_rows:
seen.setdefault(d["title"], []).append(d["id"])
for title, ids in seen.items():
if len(ids) > 1:
collisions += 1
with open(EVAL_OUT, "w", encoding="utf-8") as f:
json.dump(eval_rows, f, ensure_ascii=False, indent=0)
with open(CORPUS_OUT, "w", encoding="utf-8") as f:
json.dump(corpus_rows, f, ensure_ascii=False, indent=0)
print(f"wrote {EVAL_OUT}: {len(eval_rows)} questions")
print(f"wrote {CORPUS_OUT}: {len(corpus_rows)} documents "
f"({collisions} title-collision groups)")
from collections import Counter
dt = Counter(d["doctype"] for d in corpus_rows)
print("doctypes:", dict(dt))
if __name__ == "__main__":
main()
|