R15: Fix D04/D06/D07 partial completion

D04: Add fish name-based fallback in _auto_publish (was external_id only)
D06: Cap confidence at 50% for 1 player, 65% for 2 players
D07: Set caught_at=None for community imports (not published_at)
D08: Already OK - activity_rows has no top-100 limit

- Add Fish import to community_importer.py
- Add 2 unit tests for D06 confidence caps
- Update test_community_importer.py for D04 name match behavior
This commit is contained in:
ik
2026-09-10 05:51:50 +07:00
parent e2bed0db89
commit f550639456
5 changed files with 79 additions and 10 deletions
+52
View File
@@ -129,3 +129,55 @@ def test_reports_without_coordinates_do_not_create_activity_group(db: Session) -
db.commit()
assert activity_rows(db, hours=72, now=NOW) == []
def test_confidence_capped_at_50_with_single_player() -> None:
"""D06: One player cannot artificially inflate confidence above 50%."""
engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
Base.metadata.create_all(engine)
with Session(engine) as session:
waterbody = Waterbody(slug="test-lake", name_ru="Тестовое озеро", unlock_level=1)
fish = Fish(slug="pike", name_ru="Щука", trophy_weight_g=10_000)
spot = Spot(waterbody=waterbody, x=10, y=20)
session.add_all([waterbody, fish, spot])
session.flush()
# 10 reports from 1 player, all max confidence
for _ in range(10):
report = CatchReport(
fish=fish, spot=spot, waterbody=waterbody, bait=None,
weight_g=5_000, fishing_method="spinning",
caught_at=NOW - timedelta(hours=1), reported_at=NOW - timedelta(hours=1),
player_name="Single Player", source_type=SourceType.user,
source_confidence=100, moderation_status=ModerationStatus.approved,
)
session.add(report)
session.commit()
row = activity_rows(session, hours=24, now=NOW)[0]
assert row.unique_players == 1
assert row.confidence_score <= 50, f"Expected max 50 with 1 player, got {row.confidence_score}"
def test_confidence_capped_at_65_with_two_players() -> None:
"""D06: Two players cannot get confidence above 65%."""
engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
Base.metadata.create_all(engine)
with Session(engine) as session:
waterbody = Waterbody(slug="test-lake", name_ru="Тестовое озеро", unlock_level=1)
fish = Fish(slug="pike", name_ru="Щука", trophy_weight_g=10_000)
spot = Spot(waterbody=waterbody, x=10, y=20)
session.add_all([waterbody, fish, spot])
session.flush()
# 10 reports from 2 players, all max confidence
for i in range(10):
report = CatchReport(
fish=fish, spot=spot, waterbody=waterbody, bait=None,
weight_g=5_000, fishing_method="spinning",
caught_at=NOW - timedelta(hours=1), reported_at=NOW - timedelta(hours=1),
player_name=f"Player {i % 2}", source_type=SourceType.user,
source_confidence=100, moderation_status=ModerationStatus.approved,
)
session.add(report)
session.commit()
row = activity_rows(session, hours=24, now=NOW)[0]
assert row.unique_players == 2
assert row.confidence_score <= 65, f"Expected max 65 with 2 players, got {row.confidence_score}"
+3 -1
View File
@@ -116,9 +116,11 @@ def test_changed_published_record_requires_review_and_reuses_report(db: Session)
water = Waterbody(slug="test-lake", name_ru="Тестовое озеро")
db.add_all([fish, water])
db.commit()
# D04: auto-publish now works with name match fallback
stage_observations(db, [record() | {"weight_g": 5000}])
item = db.scalar(select(ExternalObservation))
map_observation(db, item, fish, water)
# Item auto-published via name match fallback (D04)
assert item.status == "published"
report = publish_observation(db, item)
report_id = report.id
stage_observations(db, [record() | {"weight_g": 6000}])