feat: add RF4DB and RF4-STAT research parsers

This commit is contained in:
ik
2026-09-03 18:37:09 +07:00
parent 17c9c40241
commit 16d64606f8
11 changed files with 477 additions and 7 deletions
+75
View File
@@ -0,0 +1,75 @@
from datetime import datetime, time, timezone
from pathlib import Path
import pytest
from rf4_research.community_sources import (
CommunityParseError,
parse_rf4db_catches,
parse_rf4db_detail,
parse_rf4stat_fishing,
parse_rf4stat_posts,
)
FIXTURES = Path(__file__).parent / "fixtures"
def fixture(name: str) -> str:
return (FIXTURES / name).read_text(encoding="utf-8")
def test_parses_rf4db_public_catch_cards() -> None:
row = parse_rf4db_catches(fixture("rf4db_catches_sample.html"))[0]
assert (row.source_system, row.source_external_id) == ("rf4db", "11111111-1111-4111-8111-111111111111")
assert (row.fish, row.fish_external_id) == ("Щука обыкновенная", "pike")
assert (row.waterbody, row.waterbody_external_id) == ("Тестовое озеро", "test-lake")
assert (row.x, row.y, row.weight_g) == (71, 92, None)
assert row.game_time == time(6, 14)
assert row.water_temperature_c == 12.5
def test_parses_rf4db_detail_equipment_and_cast_facts() -> None:
detail = parse_rf4db_detail(
fixture("rf4db_detail_sample.html"),
source_url="https://rf4db.com/ru/catches/11111111-1111-4111-8111-111111111111",
)
assert detail.source_external_id == "11111111-1111-4111-8111-111111111111"
assert (detail.wind, detail.line_release_m, detail.clip_m, detail.cast_direction_deg) == ("SE 4 м/с", 5.1, 5.0, 181.1)
assert [(item.kind, item.name, item.external_id) for item in detail.equipment] == [
("Приманка поклёвки", "Тестовая приманка", "test-lure"),
("Катушка", "Тестовая катушка", "test-reel"),
]
def test_rf4db_detail_rejects_unrelated_html() -> None:
with pytest.raises(CommunityParseError, match="detail not found"):
parse_rf4db_detail("<html></html>", source_url="https://rf4db.com/ru/catches/id")
def test_parses_rf4stat_fishing_rows() -> None:
row = parse_rf4stat_fishing(
fixture("rf4stat_fishing_sample.html"),
now=datetime(2026, 9, 3, 12, tzinfo=timezone.utc),
)[0]
assert (row.source_system, row.source_external_id) == ("rf4stat-fishing", "123")
assert (row.x, row.y, row.weight_g) == (71, 92, 11_584)
assert row.published_at == datetime(2026, 9, 3, 11, 27, tzinfo=timezone.utc)
assert (row.player_name, row.clip, row.fishing_style) == ("Игрок", "35", "Донная")
def test_parses_rf4stat_posts_without_using_locked_coordinates() -> None:
row = parse_rf4stat_posts(fixture("rf4stat_posts_sample.html"))[0]
assert (row.source_system, row.source_external_id) == ("rf4stat-post", "456:0")
assert (row.x, row.y, row.weight_g) == (None, None, 4_321)
assert row.evidence_urls == ("https://img.example.test/proof.jpg",)
@pytest.mark.parametrize("parser", [parse_rf4db_catches, parse_rf4stat_fishing, parse_rf4stat_posts])
def test_parsers_reject_unrelated_html(parser) -> None:
with pytest.raises(CommunityParseError):
parser("<html><body>not a source page</body></html>")