File size: 5,057 Bytes
6a5d1ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""
prepare_data.py
-----------------
ResearchPilot ships with a hand-curated corpus of 17 REAL arXiv papers on
low-resource / multilingual NLP (data/corpus.json), gathered directly from
arxiv.org so the Space works immediately with no download step and no
risk of a slow/failed dataset pull on first boot.

This script is provided so you (or a recruiter) can OPTIONALLY expand the
corpus using a real public Hugging Face dataset once the Space has
internet access (this sandbox used to build the project did not, per
docs/architecture.md).

It uses the `datasets` library to pull a slice of a public arXiv-abstracts
dataset from the Hugging Face Hub, filters it for low-resource/multilingual
NLP relevance via keyword matching, converts it into the same corpus.json
schema, and reports exactly how many real documents were added, their
source dataset name/version, and what preprocessing was applied -- per the
project's "no fabricated documents" requirement.

Usage:
    python scripts/prepare_data.py --limit 200 --out data/corpus_expanded.json
"""
import argparse
import json
import os
import re
import sys

KEYWORDS = [
    "low-resource", "low resource", "multilingual", "cross-lingual", "cross lingual",
    "under-resourced", "zero-shot translation", "few-shot", "code-switching",
    "language transfer", "endangered language",
]


def keyword_relevant(text: str) -> bool:
    t = text.lower()
    return any(k in t for k in KEYWORDS)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--dataset", default="gfissore/arxiv-abstracts-2021",
                         help="Hugging Face dataset repo id to pull real abstracts from.")
    parser.add_argument("--split", default="train")
    parser.add_argument("--limit", type=int, default=200,
                         help="Max raw rows to scan for keyword relevance (not the final doc count).")
    parser.add_argument("--max-docs", type=int, default=50, help="Cap on documents added.")
    parser.add_argument("--out", default=os.path.join(
        os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "corpus_expanded.json"))
    args = parser.parse_args()

    try:
        from datasets import load_dataset
    except ImportError:
        print("The `datasets` package is required. Install with: pip install datasets", file=sys.stderr)
        sys.exit(1)

    print(f"Loading {args.limit} rows from '{args.dataset}' split='{args.split}' (streaming)...")
    try:
        ds = load_dataset(args.dataset, split=args.split, streaming=True)
    except Exception as exc:  # noqa: BLE001
        print(f"Could not reach the Hugging Face Hub to load '{args.dataset}': {exc}", file=sys.stderr)
        print("This step requires internet access (works on a Hugging Face Space; may fail in an "
              "offline sandbox). The bundled data/corpus.json already contains 17 real papers and "
              "the app works fully without running this script.", file=sys.stderr)
        sys.exit(1)

    base_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "corpus.json")
    with open(base_path, "r", encoding="utf-8") as f:
        existing = json.load(f)
    existing_ids = {d["id"] for d in existing}

    added = []
    scanned = 0
    for row in ds:
        if scanned >= args.limit or len(added) >= args.max_docs:
            break
        scanned += 1
        title = row.get("title", "").strip()
        abstract = row.get("abstract", "").strip()
        arxiv_id = row.get("id", "") or row.get("arxiv_id", "")
        if not title or not abstract or not keyword_relevant(f"{title} {abstract}"):
            continue
        doc_id = re.sub(r"[^0-9A-Za-z.]", "", arxiv_id) or f"scraped-{len(added)}"
        if doc_id in existing_ids:
            continue
        added.append({
            "id": doc_id,
            "title": title,
            "authors": ", ".join(row.get("authors", [])) if isinstance(row.get("authors"), list) else str(row.get("authors", "")),
            "year": int(str(row.get("update_date", "0000"))[:4]) if row.get("update_date") else 0,
            "url": f"https://arxiv.org/abs/{doc_id}" if doc_id and doc_id[0].isdigit() else "",
            "topic": "auto-imported",
            "text": abstract,
        })

    combined = existing + added
    with open(args.out, "w", encoding="utf-8") as f:
        json.dump(combined, f, indent=2)

    print(f"Dataset: {args.dataset} (split={args.split})")
    print(f"Scanned {scanned} raw rows via streaming.")
    print(f"Preprocessing: kept rows with non-empty title/abstract matching low-resource/multilingual "
          f"keyword filter {KEYWORDS}; deduplicated against existing corpus ids.")
    print(f"Added {len(added)} new real documents (existing {len(existing)} preserved).")
    print(f"Wrote combined corpus of {len(combined)} documents to {args.out}")
    print("To use it, point RESEARCHPILOT_CORPUS_PATH or app.py's CORPUS_PATH at this file.")


if __name__ == "__main__":
    main()