#!/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()