fix(parsers): починить CyberLeninka + подготовить русскую заливку корпуса

Корпус на 99.4% английский, 0 русских источников — при том что сервис для русских
студентов. Корень: парсер CyberLeninka был сломан (слал GET на /api/search → HTTP
405) и ни разу не наполнял базу.

- cyberleninka.py: GET→POST с JSON-телом (mode=articles); authors теперь из списка
  (API отдаёт список, не строку); чистка <b>-подсветки и HTML-сущностей (&quot;).
  Проверено вживую: 5/5 студенческих тем возвращают реальные русские статьи.
- Юнит-тесты парсера (scripts/parsers/tests/, 5 шт.) + обвязка; run_tests.sh обобщён
  на пути → парсеры теперь в тест-гейте CI. Всего тестов: 87.
- scripts/seed_ru_sources.py: сидер parse_sources по 30 студенческим дисциплинам
  (dry-run по умолчанию, --apply для записи). НЕ запускает заливку — готовит задания.
- docs/INGESTION.md: runbook (текущее состояние, шаги запуска, проверка, масштаб).

Прод-путь index.run_parser уже поддерживает cyberleninka и openalex(lang=ru).
Заливку не запускал — это отдельный go (ресурсоёмко: GPU-эмбеддинги, рост БД).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jze9
2026-08-12 21:02:16 +05:00
parent e6c44f30dd
commit 9d005486df
8 changed files with 292 additions and 21 deletions

View File

@@ -17,22 +17,24 @@ MIRROR="${PIP_INDEX_URL:-https://pypi.tuna.tsinghua.edu.cn/simple/}"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
IMAGE="python:3.11-slim"
# service:apt-пакеты (нужны faiss-cpu → libgomp1)
# относительный-путь:apt-пакеты (нужны faiss-cpu → libgomp1)
SERVICES=(
"worker-indexer:"
"worker-gost:"
"worker-gpu:libgomp1"
"services/worker-indexer:"
"services/worker-gost:"
"services/worker-gpu:libgomp1"
"scripts/parsers:"
)
run_service() {
local svc="$1" apt_pkgs="$2"
local dir="$ROOT/services/$svc"
local dir_rel="$1" apt_pkgs="$2"
local dir="$ROOT/$dir_rel"
local name="${dir_rel##*/}"
if [[ ! -f "$dir/requirements-test.txt" ]]; then
echo "⚠ $svc: нет requirements-test.txt — пропуск"
echo "⚠ $name: нет requirements-test.txt — пропуск"
return 0
fi
echo "──────────────────────────────────────────────"
echo "▶ Тесты: $svc"
echo "▶ Тесты: $name"
echo "──────────────────────────────────────────────"
docker run --rm \
-v "$dir":/app -w /app \
@@ -49,12 +51,13 @@ run_service() {
FILTER="${1:-}"
failed=0
for entry in "${SERVICES[@]}"; do
svc="${entry%%:*}"
dir_rel="${entry%%:*}"
apt="${entry#*:}"
[[ -n "$FILTER" && "$FILTER" != "$svc" ]] && continue
if ! run_service "$svc" "$apt"; then
name="${dir_rel##*/}"
[[ -n "$FILTER" && "$FILTER" != "$name" ]] && continue
if ! run_service "$dir_rel" "$apt"; then
failed=1
echo "✗ $svc: тесты упали"
echo "✗ $name: тесты упали"
fi
done