feat(ingestion): расширение охвата корпуса — 64 новые дисциплины
Продолжение seed_ru_sources.py: все 30 базовых RU-тем + 26 EN-тем (OpenAlex) + 4 категории arXiv уже отработаны (done). Этот скрипт добавляет НОВОЕ поверх — углубление RU (специализированные подотрасли права/экономики/IT, 29 тем) и расширение EN на области, которых не было (право, лингвистика, история, искусство, науки о Земле и т.д., 20 тем) + 15 новых тем arXiv. Идемпотентно (префиксы ru2:/oa3:/arxiv2: не пересекаются с уже существующими). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
134
scripts/seed_broad_corpus.py
Normal file
134
scripts/seed_broad_corpus.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Расширение корпуса вширь — новые дисциплины, которых ещё нет в parse_sources.
|
||||||
|
|
||||||
|
Продолжение scripts/seed_ru_sources.py: та заливка покрыла 30 базовых русских
|
||||||
|
дисциплин + был уже собран seed-корпус из ~26 английских тем (OpenAlex) и 4
|
||||||
|
категорий arXiv. Этот скрипт добавляет НОВЫЕ темы поверх уже сделанного —
|
||||||
|
углубление RU (специализированные подотрасли права/экономики/IT) и расширение EN
|
||||||
|
(область права/лингвистики/искусства/наук о Земле и т.д., которых ещё не было),
|
||||||
|
плюс новые категории arXiv.
|
||||||
|
|
||||||
|
Идемпотентно (по name, с префиксами ru2:/oa3:/arxiv2: — не пересекаются с уже
|
||||||
|
существующими ru:/corpus:/oa2:/arxiv:). Dry-run по умолчанию.
|
||||||
|
|
||||||
|
Запуск:
|
||||||
|
python scripts/seed_broad_corpus.py # план
|
||||||
|
python scripts/seed_broad_corpus.py --apply # записать в БД
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# (name_suffix, source_type, query, lang, limit)
|
||||||
|
RU_DEEP = [ # углубление: специализированные RU-темы, которых не было в первой волне
|
||||||
|
"семейное право", "административное право", "уголовный процесс",
|
||||||
|
"гражданский процесс", "международное право", "предпринимательское право",
|
||||||
|
"налоговое право", "земельное право", "экологическое право",
|
||||||
|
"информационная безопасность", "искусственный интеллект", "нейронные сети",
|
||||||
|
"веб-разработка", "архитектура зданий", "дизайн", "музыкальное искусство",
|
||||||
|
"физическая культура", "спортивная медицина", "туризм", "логистика",
|
||||||
|
"инновационный менеджмент", "антикризисное управление", "аудит",
|
||||||
|
"страхование", "банковское дело", "инвестиции",
|
||||||
|
"внешнеэкономическая деятельность", "региональная экономика", "демография",
|
||||||
|
]
|
||||||
|
|
||||||
|
EN_OPENALEX_NEW = [ # области, которых не было в первой EN-волне
|
||||||
|
"law", "political science", "linguistics", "literature studies", "history",
|
||||||
|
"philosophy", "anthropology", "geography", "agriculture",
|
||||||
|
"business management", "finance", "architecture", "astronomy", "geology",
|
||||||
|
"pharmacology", "nursing", "veterinary science", "art history",
|
||||||
|
"music theory", "religious studies",
|
||||||
|
]
|
||||||
|
|
||||||
|
EN_ARXIV_NEW = [ # темы поиска arXiv, не пересекаются с уже сделанными 4
|
||||||
|
"natural language processing", "reinforcement learning", "quantum physics",
|
||||||
|
"particle physics", "condensed matter physics", "number theory",
|
||||||
|
"statistics methodology", "signal processing", "distributed systems",
|
||||||
|
"software engineering", "bioinformatics", "econometrics", "optimization",
|
||||||
|
"graph theory", "information theory",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _load_env() -> None:
|
||||||
|
env = Path(__file__).resolve().parent.parent / ".env"
|
||||||
|
if not env.exists():
|
||||||
|
return
|
||||||
|
for line in env.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
key, _, val = line.partition("=")
|
||||||
|
os.environ.setdefault(key.strip(), val.strip())
|
||||||
|
|
||||||
|
|
||||||
|
def _pg_dsn() -> dict:
|
||||||
|
return {
|
||||||
|
"host": os.environ.get("POSTGRES_HOST", "localhost"),
|
||||||
|
"port": int(os.environ.get("POSTGRES_PORT", "5432")),
|
||||||
|
"dbname": os.environ.get("POSTGRES_DB", "antiplagiator"),
|
||||||
|
"user": os.environ.get("POSTGRES_USER", "antiplagiator"),
|
||||||
|
"password": os.environ.get("POSTGRES_PASSWORD", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_rows(ru_limit: int, en_limit: int, arxiv_limit: int) -> list[tuple]:
|
||||||
|
rows = []
|
||||||
|
rows += [(f"ru2:{t}", "cyberleninka", t, "ru", ru_limit) for t in RU_DEEP]
|
||||||
|
rows += [(f"oa3:{t}", "openalex", t, None, en_limit) for t in EN_OPENALEX_NEW]
|
||||||
|
rows += [(f"arxiv2:{t}", "arxiv", t, None, arxiv_limit) for t in EN_ARXIV_NEW]
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
ap.add_argument("--apply", action="store_true", help="реально записать в БД (иначе dry-run)")
|
||||||
|
ap.add_argument("--ru-limit", type=int, default=500)
|
||||||
|
ap.add_argument("--en-limit", type=int, default=1000)
|
||||||
|
ap.add_argument("--arxiv-limit", type=int, default=800)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
_load_env()
|
||||||
|
rows = _build_rows(args.ru_limit, args.en_limit, args.arxiv_limit)
|
||||||
|
total_potential = sum(r[4] for r in rows)
|
||||||
|
|
||||||
|
print(f"Новых источников: {len(rows)} "
|
||||||
|
f"(RU углубление {len(RU_DEEP)}, EN OpenAlex {len(EN_OPENALEX_NEW)}, "
|
||||||
|
f"arXiv {len(EN_ARXIV_NEW)})")
|
||||||
|
print(f"Потенциальный объём (верхняя граница): ~{total_potential} документов")
|
||||||
|
|
||||||
|
if not args.apply:
|
||||||
|
print("\n[dry-run] запустите с --apply, чтобы записать в parse_sources.")
|
||||||
|
for name, stype, q, _lang, lim in rows[:10]:
|
||||||
|
print(f" {name:32} [{stype:12}] limit={lim} → {q!r}")
|
||||||
|
print(f" ... и ещё {len(rows) - 10}")
|
||||||
|
return
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
conn = psycopg2.connect(**_pg_dsn())
|
||||||
|
conn.autocommit = True
|
||||||
|
created, skipped, ids = 0, 0, []
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
for name, stype, query, lang, limit in rows:
|
||||||
|
cur.execute("SELECT id FROM parse_sources WHERE name = %s", (name,))
|
||||||
|
if cur.fetchone():
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
cur.execute(
|
||||||
|
'INSERT INTO parse_sources '
|
||||||
|
'(source_type, name, query, lang, "limit", enabled, last_status, docs_added) '
|
||||||
|
"VALUES (%s, %s, %s, %s, %s, TRUE, 'idle', 0) RETURNING id",
|
||||||
|
(stype, name, query, lang, limit),
|
||||||
|
)
|
||||||
|
ids.append(cur.fetchone()[0])
|
||||||
|
created += 1
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
print(f"\nСоздано: {created}, пропущено (уже были): {skipped}")
|
||||||
|
print(f"ID новых источников: {ids}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user