Skip to content

Commit eb0f8a6

Browse files
committed
enrichment check and states
1 parent ffdc198 commit eb0f8a6

File tree

7 files changed

+108
-62
lines changed

7 files changed

+108
-62
lines changed

poliloom-gui/src/app/evaluate/page.test.tsx

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,43 +51,39 @@ describe('Evaluate Page', () => {
5151
render(<EvaluatePage />)
5252
})
5353

54-
expect(screen.getByText('Loading politician data...')).toBeInTheDocument()
54+
expect(screen.getByText('Finding politicians...')).toBeInTheDocument()
5555
expect(screen.getByText('Header')).toBeInTheDocument()
5656
})
5757

58-
it('shows no politicians message when not loading and no politician available', async () => {
59-
const mockLoadPoliticians = vi.fn()
58+
it('shows all caught up message when no politicians and nothing to enrich', async () => {
6059
mockUseEvaluationSession.mockReturnValue({
6160
currentPolitician: null,
6261
nextPolitician: null,
6362
loading: false,
64-
enrichmentMeta: { is_enriching: false, total_matching_filters: 0 },
63+
enrichmentMeta: { has_enrichable_politicians: false, total_matching_filters: 0 },
6564
completedCount: 0,
6665
sessionGoal: 1,
6766
isSessionComplete: false,
6867
submitEvaluation: vi.fn(),
6968
skipPolitician: vi.fn(),
7069
resetSession: vi.fn(),
71-
loadPoliticians: mockLoadPoliticians,
70+
loadPoliticians: vi.fn(),
7271
})
7372

7473
await act(async () => {
7574
render(<EvaluatePage />)
7675
})
7776

78-
expect(
79-
screen.getByText(/No politicians available for your current filters/),
80-
).toBeInTheDocument()
81-
expect(screen.getByText('filters')).toBeInTheDocument()
82-
expect(screen.getByText('reload')).toBeInTheDocument()
77+
expect(screen.getByText("You're all caught up!")).toBeInTheDocument()
78+
expect(screen.getByText('Start New Session')).toBeInTheDocument()
8379
})
8480

85-
it('shows enriching spinner when no politician but enrichment is running', async () => {
81+
it('shows loading state when no politician but more can be enriched', async () => {
8682
mockUseEvaluationSession.mockReturnValue({
8783
currentPolitician: null,
8884
nextPolitician: null,
8985
loading: false,
90-
enrichmentMeta: { is_enriching: true, total_matching_filters: 0 },
86+
enrichmentMeta: { has_enrichable_politicians: true, total_matching_filters: 0 },
9187
completedCount: 0,
9288
sessionGoal: 1,
9389
isSessionComplete: false,
@@ -101,7 +97,7 @@ describe('Evaluate Page', () => {
10197
render(<EvaluatePage />)
10298
})
10399

104-
expect(screen.getByText('Enriching politician data...')).toBeInTheDocument()
100+
expect(screen.getByText('Finding politicians...')).toBeInTheDocument()
105101
})
106102

107103
it('shows PoliticianEvaluation component when politician data is available', async () => {

poliloom-gui/src/app/evaluate/page.tsx

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { useRouter } from 'next/navigation'
55
import { Header } from '@/components/layout/Header'
66
import { PoliticianEvaluation } from '@/components/evaluation/PoliticianEvaluation'
77
import { useEvaluationSession } from '@/contexts/EvaluationSessionContext'
8-
import { Loader } from '@/components/ui/Spinner'
8+
import { CenteredCard } from '@/components/ui/CenteredCard'
9+
import { Button } from '@/components/ui/Button'
10+
import { Spinner } from '@/components/ui/Spinner'
911
import { useTutorial } from '@/contexts/TutorialContext'
1012
import { useUserPreferences } from '@/contexts/UserPreferencesContext'
11-
import Link from 'next/link'
1213

1314
export default function EvaluatePage() {
1415
const router = useRouter()
15-
const { currentPolitician, loading, loadPoliticians, isSessionComplete, enrichmentMeta } =
16-
useEvaluationSession()
16+
const { currentPolitician, loading, isSessionComplete, enrichmentMeta } = useEvaluationSession()
1717
const { completeBasicTutorial, completeAdvancedTutorial } = useTutorial()
1818
const { isAdvancedMode } = useUserPreferences()
1919

@@ -33,44 +33,61 @@ export default function EvaluatePage() {
3333
}
3434
}, [isSessionComplete, router])
3535

36-
return (
37-
<>
38-
<Header />
36+
// Determine if we're in a loading state (fetching or waiting for enrichment)
37+
const isWaitingForData =
38+
loading || (enrichmentMeta?.has_enrichable_politicians !== false && !currentPolitician)
3939

40-
{currentPolitician ? (
40+
// All caught up: no politician and nothing left to enrich
41+
const isAllCaughtUp =
42+
!currentPolitician && !loading && enrichmentMeta?.has_enrichable_politicians === false
43+
44+
if (currentPolitician) {
45+
return (
46+
<>
47+
<Header />
4148
<PoliticianEvaluation key={currentPolitician.id} politician={currentPolitician} />
42-
) : (
43-
<main className="bg-gray-50 grid place-items-center py-12 px-4 sm:px-6 lg:px-8 min-h-0 overflow-y-auto">
44-
<div className="text-center max-w-2xl">
45-
{loading || enrichmentMeta?.is_enriching ? (
46-
<Loader
47-
message={
48-
enrichmentMeta?.is_enriching
49-
? 'Enriching politician data...'
50-
: 'Loading politician data...'
51-
}
52-
/>
53-
) : (
54-
<div className="bg-gray-50 border border-gray-200 rounded-md p-4">
55-
<p className="text-gray-600">
56-
No politicians available for your current filters. You can change your{' '}
57-
<Link href="/" className="text-gray-700 hover:text-gray-900 underline">
58-
filters
59-
</Link>
60-
, or{' '}
61-
<button
62-
onClick={loadPoliticians}
63-
className="text-gray-700 hover:text-gray-900 underline cursor-pointer bg-transparent border-0 p-0 font-inherit"
64-
>
65-
reload
66-
</button>
67-
.
68-
</p>
69-
</div>
70-
)}
49+
</>
50+
)
51+
}
52+
53+
if (isAllCaughtUp) {
54+
return (
55+
<>
56+
<Header />
57+
<CenteredCard emoji="🎉" title="You're all caught up!">
58+
<p className="mb-6">
59+
No more politicians to evaluate for your current filters. Try different filters to
60+
continue contributing.
61+
</p>
62+
<Button href="/" size="large">
63+
Start New Session
64+
</Button>
65+
</CenteredCard>
66+
</>
67+
)
68+
}
69+
70+
if (isWaitingForData) {
71+
return (
72+
<>
73+
<Header />
74+
<CenteredCard emoji="🔍" title="Finding politicians...">
75+
<div className="flex justify-center">
76+
<Spinner />
7177
</div>
72-
</main>
73-
)}
78+
</CenteredCard>
79+
</>
80+
)
81+
}
82+
83+
return (
84+
<>
85+
<Header />
86+
<CenteredCard emoji="🔍" title="Loading...">
87+
<div className="flex justify-center">
88+
<Spinner />
89+
</div>
90+
</CenteredCard>
7491
</>
7592
)
7693
}

poliloom-gui/src/contexts/EvaluationSessionContext.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,18 +137,18 @@ export function EvaluationSessionProvider({ children }: { children: React.ReactN
137137
// eslint-disable-next-line react-hooks/exhaustive-deps
138138
}, [isAuthenticated, initialized, currentPolitician, loadPoliticians])
139139

140-
// Auto-poll when no politicians available but enrichment is running
140+
// Auto-poll when no politicians available but more can be enriched
141141
useEffect(() => {
142142
if (currentPolitician !== null) return
143143
if (loading) return
144-
if (!enrichmentMeta?.is_enriching) return
144+
if (!enrichmentMeta?.has_enrichable_politicians) return
145145

146146
const pollInterval = setInterval(() => {
147147
loadPoliticians()
148148
}, 5000)
149149

150150
return () => clearInterval(pollInterval)
151-
}, [currentPolitician, loading, enrichmentMeta?.is_enriching, loadPoliticians])
151+
}, [currentPolitician, loading, enrichmentMeta?.has_enrichable_politicians, loadPoliticians])
152152

153153
const advanceToNextPolitician = useCallback(async () => {
154154
const newCurrent = nextPolitician

poliloom-gui/src/types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export interface Politician {
5454
}
5555

5656
export interface EnrichmentMetadata {
57-
is_enriching: boolean
57+
has_enrichable_politicians: boolean
5858
total_matching_filters: number
5959
}
6060

poliloom/poliloom/api/evaluations.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
from sqlalchemy.orm import Session, selectinload
1313

1414
from ..database import get_db_session
15-
from ..enrichment import count_politicians_with_unevaluated, enrich_batch
15+
from ..enrichment import (
16+
count_politicians_with_unevaluated,
17+
enrich_batch,
18+
has_enrichable_politicians,
19+
)
1620
from ..models import (
1721
ArchivedPage,
1822
ArchivedPageLanguage,
@@ -146,9 +150,11 @@ async def get_politicians_for_evaluation(
146150
# Track enrichment status for empty state UX
147151
min_threshold = int(os.getenv("MIN_UNEVALUATED_POLITICIANS", "10"))
148152
current_count = count_politicians_with_unevaluated(db, languages, countries)
149-
is_enriching = False
150153

151-
if current_count < min_threshold:
154+
# Check if there are politicians available to enrich (for "all caught up" state)
155+
can_enrich = has_enrichable_politicians(db, languages, countries)
156+
157+
if current_count < min_threshold and can_enrich:
152158
logger.info(
153159
f"Only {current_count} politicians with unevaluated properties (threshold: {min_threshold}), triggering enrichment batch"
154160
)
@@ -159,7 +165,6 @@ async def get_politicians_for_evaluation(
159165
languages,
160166
countries,
161167
)
162-
is_enriching = True
163168

164169
result = []
165170
for politician in politicians:
@@ -206,7 +211,7 @@ async def get_politicians_for_evaluation(
206211
return PoliticiansListResponse(
207212
politicians=result,
208213
meta=EnrichmentMetadata(
209-
is_enriching=is_enriching,
214+
has_enrichable_politicians=can_enrich,
210215
total_matching_filters=current_count,
211216
),
212217
)

poliloom/poliloom/api/schemas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class PoliticianResponse(UUIDBaseModel):
5959
class EnrichmentMetadata(BaseModel):
6060
"""Metadata about enrichment status for empty state UX."""
6161

62-
is_enriching: bool = False
62+
has_enrichable_politicians: bool = True
6363
total_matching_filters: int = 0
6464

6565

poliloom/poliloom/enrichment.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,34 @@ def count_politicians_with_unevaluated(
647647
return result or 0
648648

649649

650+
def has_enrichable_politicians(
651+
db: Session,
652+
languages: Optional[List[str]] = None,
653+
countries: Optional[List[str]] = None,
654+
) -> bool:
655+
"""
656+
Check if there are politicians available to enrich.
657+
658+
Uses the same query logic as enrich_politician_from_wikipedia to determine
659+
if any politicians can be enriched (not enriched within last 6 months).
660+
661+
Args:
662+
db: Database session
663+
languages: Optional list of language QIDs to filter by
664+
countries: Optional list of country QIDs to filter by
665+
666+
Returns:
667+
True if there are politicians available to enrich, False otherwise
668+
"""
669+
query = Politician.query_for_enrichment(
670+
languages=languages,
671+
countries=countries,
672+
).limit(1)
673+
674+
result = db.execute(query).first()
675+
return result is not None
676+
677+
650678
def enrich_batch(
651679
languages: Optional[List[str]] = None,
652680
countries: Optional[List[str]] = None,

0 commit comments

Comments
 (0)