feat: add RF4MAP and RF4 Posts research parsers
CI / backend-and-migrations (push) Canceled after 0s
CI / astro-build (push) Canceled after 0s
CI / compose-e2e (push) Canceled after 0s

This commit is contained in:
ik
2026-09-05 07:36:35 +07:00
parent 9cc3d44a49
commit 1a09bd59f3
9 changed files with 230 additions and 7 deletions
+48
View File
@@ -7,6 +7,8 @@ from rf4_research.community_sources import (
CommunityParseError,
parse_rf4db_catches,
parse_rf4db_detail,
parse_rf4map_point,
parse_rf4posts_spot,
parse_rf4stat_fishing,
parse_rf4stat_posts,
)
@@ -69,7 +71,53 @@ def test_parses_rf4stat_posts_without_using_locked_coordinates() -> None:
assert row.evidence_urls == ("https://img.example.test/proof.jpg",)
def test_parses_rf4map_individual_observations_into_common_contract() -> None:
rows = parse_rf4map_point(
fixture("rf4map_point_sample.html"), source_url="https://rf4map.ru/points/275",
)
assert len(rows) == 2
assert (rows[0].source_system, rows[0].source_external_id) == ("rf4map", "37100")
assert (rows[0].fish, rows[0].fish_external_id) == ("Линь", "98")
assert (rows[0].waterbody, rows[0].waterbody_external_id) == ("оз. Комариное", "16")
assert (rows[0].x, rows[0].y, rows[0].weight_g, rows[0].clip) == (53, 87, None, "8")
assert rows[1].bait == "Тесто медовое"
assert rows[1].evidence_urls == ("https://img.example.test/proof.jpg",)
def test_parses_rf4posts_aggregate_spot_without_inventing_weights() -> None:
rows = parse_rf4posts_spot(
fixture("rf4posts_spot_sample.html"),
source_url="https://rf4-posts.com/ru/spots/d0c6d9c6-4ebf-49a7-98a8-9a562553a8ee",
)
assert [row.fish for row in rows] == ["Карп чешуйчатый", "Линь"]
assert rows[0].source_external_id.endswith(":carp")
assert (rows[0].x, rows[0].y, rows[0].weight_g) == (132, 147, None)
assert (rows[0].rig_type, rows[0].fishing_style, rows[0].clip) == ("method_popup", "feeder", "8")
assert rows[0].evidence_urls == ("https://img.example.test/spot.png",)
def test_new_sources_keep_source_namespaces_for_compatible_records() -> None:
rf4map = parse_rf4map_point(
fixture("rf4map_point_sample.html"), source_url="https://rf4map.ru/points/275",
)[0]
rf4posts = parse_rf4posts_spot(
fixture("rf4posts_spot_sample.html"), source_url="https://rf4-posts.com/ru/spots/example",
)[1]
assert rf4map.fish == rf4posts.fish == "Линь"
assert rf4map.source_system != rf4posts.source_system
assert rf4map.source_external_id != rf4posts.source_external_id
@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>")
@pytest.mark.parametrize("parser", [parse_rf4map_point, parse_rf4posts_spot])
def test_detail_parsers_reject_unrelated_html(parser) -> None:
with pytest.raises(CommunityParseError):
parser("<html><body>not a source page</body></html>", source_url="https://example.test/item/1")