Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions backend/app/services/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,19 @@ async def get_or_issue(
latest_call_at = max(
(aware_datetime(call.ended_at) for call in calls if call.ended_at), default=None
)
if existing and (
to_date < date.today()
or latest_call_at is None
or latest_call_at <= aware_datetime(existing.issued_at)
analyzed_ids = [call.id for call in calls if call.state == "ANALYZED"]
# 분석은 통화가 끝나고 몇 분 뒤에 끝난다. 그 사이에 만든 스냅샷은 종료 시각만 보면
# 최신으로 보이므로, 분석된 통화 수가 달라졌으면(지난 기간도) 다시 만든다.
if (
existing
and existing.snapshot.get("analyzedCallCount") == len(analyzed_ids)
and (
to_date < date.today()
or latest_call_at is None
or latest_call_at <= aware_datetime(existing.issued_at)
)
):
return {**existing.snapshot, "issuedAt": existing.issued_at}
analyzed_ids = [call.id for call in calls if call.state == "ANALYZED"]
extractions = []
features = []
signals = []
Expand Down
84 changes: 84 additions & 0 deletions backend/tests/test_report_snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from __future__ import annotations

from collections.abc import AsyncIterator
from datetime import UTC, datetime, timedelta
from pathlib import Path

import pytest
from sqlalchemy import update

from app.database import Database
from app.models import CallRecord, CallState, User
from app.services.reports import ReportService, aware_datetime, period_bounds


@pytest.fixture
async def database(tmp_path: Path) -> AsyncIterator[Database]:
database = Database(f"sqlite+aiosqlite:///{tmp_path / 'snapshot.db'}")
await database.ensure_schema(auto_reset=True)
yield database
await database.close()


async def seed_processing_call(database: Database, ended_at: datetime) -> tuple[str, str]:
async with database.sessions() as session:
parent = User(phone="01012345678", name="Parent", role="PARENT")
child = User(phone="01087654321", name="Child", role="CHILD")
session.add_all([parent, child])
await session.flush()
call = CallRecord(
parent_id=parent.id,
child_id=child.id,
room_name="snapshot-test",
state=CallState.PROCESSING.value,
ended_at=ended_at,
)
session.add(call)
await session.commit()
return call.id, parent.id


async def finish_analysis(database: Database, call_id: str) -> None:
async with database.sessions() as session:
await session.execute(
update(CallRecord)
.where(CallRecord.id == call_id)
.values(state=CallState.ANALYZED.value)
)
await session.commit()


async def weekly(database: Database, parent_id: str, target) -> dict:
async with database.sessions() as session:
return await ReportService().get_or_issue(session, parent_id, "WEEKLY", target)


@pytest.mark.asyncio
async def test_report_issued_while_analysing_picks_up_the_call(database: Database) -> None:
# 통화가 끝나고 분석이 도는 동안 리포트를 열면 스냅샷이 먼저 저장된다. 분석이 끝난 뒤에는
# 그 통화의 종료 시각이 스냅샷보다 이르더라도 다시 만들어 새 분석을 넣어야 한다.
ended = datetime.now(UTC) - timedelta(minutes=5)
call_id, parent_id = await seed_processing_call(database, ended)
first = await weekly(database, parent_id, None)
assert first["analyzedCallCount"] == 0

await finish_analysis(database, call_id)
second = await weekly(database, parent_id, None)
assert second["analyzedCallCount"] == 1

# 바뀐 것이 없으면 저장된 스냅샷을 그대로 준다.
third = await weekly(database, parent_id, None)
assert aware_datetime(third["issuedAt"]) == aware_datetime(second["issuedAt"])


@pytest.mark.asyncio
async def test_past_week_report_picks_up_late_analysis(database: Database) -> None:
# 일요일 밤 통화의 분석이 월요일에 끝나도 지난주 리포트에 들어가야 한다.
last_week_start, _ = period_bounds("WEEKLY", (datetime.now(UTC) - timedelta(days=7)).date())
ended = datetime.combine(last_week_start + timedelta(days=2), datetime.min.time(), tzinfo=UTC)
call_id, parent_id = await seed_processing_call(database, ended)
target = last_week_start + timedelta(days=2)
assert (await weekly(database, parent_id, target))["analyzedCallCount"] == 0

await finish_analysis(database, call_id)
assert (await weekly(database, parent_id, target))["analyzedCallCount"] == 1
Loading