Files
anti-plagiarism/scripts/seed_ru_sources.py
jze9 729b41bbfb
All checks were successful
Deploy / test (push) Successful in 3m5s
Deploy / deploy (push) Successful in 2s
feat(corpus): добавить прикладные/техникумовские темы в русский сидинг
Исходный список из 29 дисциплин был чисто вузовски-научным (экономика,
право, психология и т.д.) — ни одной темы уровня СПО/колледжей
(полиграфия, строительство, сварка, туризм и т.п.), хотя реальные
дипломы студентов именно на них. +29 новых дисциплин, идемпотентно
(старые 30 не трогает).
2026-08-25 15:52:12 +05:00

133 lines
7.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Подготовка русскоязычной заливки корпуса — сидинг parse_sources (CyberLeninka).
Зачем: корпус на ~99% английский (0 русских источников), хотя сервис — для русских
студентов. Этот скрипт создаёт в таблице `parse_sources` набор заданий CyberLeninka
по основным студенческим дисциплинам. Он НЕ запускает заливку — только готовит
задания; их затем триггерят (кнопка «Запустить» в админке или Celery-задача
`index.run_parser(source_id)`).
Идемпотентно: источник с таким `name` повторно не создаётся.
Запуск:
# dry-run — показать план, ничего не писать:
python scripts/seed_ru_sources.py
# создать источники в БД (лимит на каждую дисциплину):
python scripts/seed_ru_sources.py --apply --limit 500
Креды PG берутся из .env (POSTGRES_HOST/PORT/DB/USER/PASSWORD) или окружения.
ВНИМАНИЕ: локальный .env может указывать на старый PG — для прода используйте
POSTGRES_HOST=192.168.1.38 (см. вывод по наполненности базы).
"""
import argparse
import os
import sys
from pathlib import Path
# Студенческие дисциплины → поисковый запрос в CyberLeninka (русскоязычный).
DISCIPLINES = [
"экономический анализ", "менеджмент организации", "бухгалтерский учёт",
"финансы и кредит", "маркетинг", "гражданское право", "уголовное право",
"конституционное право", "трудовое право", "педагогика", "психология личности",
"социология", "философия науки", "история России", "политология",
"информационные технологии", "программирование", "базы данных",
"математический анализ", "физика", "химия", "биология", "медицина",
"экология", "лингвистика", "литературоведение", "государственное управление",
"международные отношения", "журналистика", "культурология",
# Прикладные/техникумовские темы — оригинальный список был вузовски-научным,
# тут реальные дипломы СПО/колледжей, которых там не было вовсе.
"полиграфия и печать", "издательское дело", "дизайн", "строительство",
"архитектура", "машиностроение", "электротехника", "логистика", "туризм",
"гостиничное дело", "сестринское дело", "ветеринария", "агрономия",
"пищевая промышленность", "транспорт", "дорожное строительство",
"сварочное производство", "метрология и стандартизация", "реклама и PR",
"физическая культура и спорт", "дошкольное образование", "дефектология",
"банковское дело", "таможенное дело", "документоведение",
"телекоммуникации", "нефтегазовое дело", "энергетика",
"пожарная безопасность", "социальная работа",
]
def _load_env() -> None:
"""Подтянуть переменные из .env репозитория, если файл есть."""
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 main() -> None:
ap = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--apply", action="store_true", help="реально записать в БД (иначе dry-run)")
ap.add_argument("--limit", type=int, default=500, help="лимит документов на дисциплину")
ap.add_argument("--source-type", default="cyberleninka", help="тип парсера (по умолчанию cyberleninka)")
args = ap.parse_args()
_load_env()
rows = [(f"ru:{d}", args.source_type, d) for d in DISCIPLINES]
print(f"Дисциплин: {len(rows)} | тип: {args.source_type} | лимит/шт: {args.limit}")
print(f"Потенциальный объём: ~{len(rows) * args.limit} документов")
if not args.apply:
print("\n[dry-run] будут созданы источники (name → query):")
for name, _, q in rows:
print(f" {name:38} → {q!r}")
print("\nЗапустите с --apply, чтобы записать в parse_sources.")
return
try:
import psycopg2
except ImportError:
sys.exit("Нужен psycopg2 (есть в образах воркеров). Запускайте на app-хосте/в контейнере.")
dsn = _pg_dsn()
print(f"\nПодключение к PG {dsn['host']}:{dsn['port']}/{dsn['dbname']} ...")
conn = psycopg2.connect(**dsn)
conn.autocommit = True
created, skipped, ids = 0, 0, []
with conn.cursor() as cur:
for name, stype, query in rows:
cur.execute("SELECT id FROM parse_sources WHERE name = %s", (name,))
existing = cur.fetchone()
if existing:
skipped += 1
continue
cur.execute(
'INSERT INTO parse_sources '
'(source_type, name, query, lang, "limit", enabled, last_status, docs_added) '
"VALUES (%s, %s, %s, 'ru', %s, TRUE, 'idle', 0) RETURNING id",
(stype, name, query, args.limit),
)
ids.append(cur.fetchone()[0])
created += 1
conn.close()
print(f"Создано: {created}, пропущено (уже были): {skipped}")
if ids:
print(f"ID новых источников: {ids}")
print("\nЗапуск заливки (после проверки источников):")
print(" • Админка → раздел «Источники» → «Запустить» у каждого, ЛИБО")
print(" • Celery: for i in ids: index.run_parser.delay(i)")
print("Рекомендация: сначала запустить 1-2 для проверки темпа/качества, потом остальные.")
if __name__ == "__main__":
main()