Filter recent requests by resolved display stage before pagination
This commit is contained in:
@@ -2760,14 +2760,17 @@ async def recent_requests(
|
|||||||
status_codes = request_stage_filter_codes(stage)
|
status_codes = request_stage_filter_codes(stage)
|
||||||
if _recent_cache_stale():
|
if _recent_cache_stale():
|
||||||
_refresh_recent_cache_from_db()
|
_refresh_recent_cache_from_db()
|
||||||
|
# Jellyfin can promote working requests to ready. Filter the displayed
|
||||||
|
# stage before pagination, including working candidates in the ready view.
|
||||||
|
candidate_codes = [4, 5] if status_codes == [4] else status_codes
|
||||||
|
take = max(1, min(int(take), 200))
|
||||||
|
skip = max(0, int(skip))
|
||||||
rows = _get_recent_from_cache(
|
rows = _get_recent_from_cache(
|
||||||
requested_by,
|
requested_by, requested_by_id,
|
||||||
requested_by_id,
|
len(_recent_cache.get("items") or []), 0, since_iso,
|
||||||
take,
|
status_codes=candidate_codes,
|
||||||
skip,
|
|
||||||
since_iso,
|
|
||||||
status_codes=status_codes,
|
|
||||||
)
|
)
|
||||||
|
matched = 0
|
||||||
cache_mode = (runtime.artwork_cache_mode or "remote").lower()
|
cache_mode = (runtime.artwork_cache_mode or "remote").lower()
|
||||||
allow_title_hydrate = False
|
allow_title_hydrate = False
|
||||||
allow_artwork_hydrate = client.configured()
|
allow_artwork_hydrate = client.configured()
|
||||||
@@ -2880,6 +2883,13 @@ async def recent_requests(
|
|||||||
jellyfin_cache,
|
jellyfin_cache,
|
||||||
)
|
)
|
||||||
status_label = _status_label_with_jellyfin(status, is_available)
|
status_label = _status_label_with_jellyfin(status, is_available)
|
||||||
|
if status_label == STATUS_LABELS[4]:
|
||||||
|
status = 4
|
||||||
|
if status_codes and status not in status_codes:
|
||||||
|
continue
|
||||||
|
matched += 1
|
||||||
|
if matched <= skip:
|
||||||
|
continue
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
"id": row.get("request_id"),
|
"id": row.get("request_id"),
|
||||||
@@ -2896,6 +2906,8 @@ async def recent_requests(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if len(results) >= take:
|
||||||
|
break
|
||||||
|
|
||||||
return {"results": results}
|
return {"results": results}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import unittest
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
from contextlib import ExitStack
|
||||||
|
from backend.app.routers import requests
|
||||||
|
|
||||||
|
|
||||||
|
class RecentStageTests(unittest.IsolatedAsyncioTestCase):
|
||||||
|
async def test_displayed_stage_controls_filter_and_pagination(self):
|
||||||
|
runtime = SimpleNamespace(jellyseerr_base_url='', jellyseerr_api_key='',
|
||||||
|
requests_data_source='prefer_cache', artwork_cache_mode='remote',
|
||||||
|
jellyfin_base_url='', jellyfin_api_key='')
|
||||||
|
rows = [dict(request_id=i, title=str(i), status=status, media_type='movie',
|
||||||
|
requested_by_id=10) for i, status in [(1,5),(2,5),(3,4),(4,6),(5,5),(6,2),(7,1),(8,3)]]
|
||||||
|
async def available(client, title, *args): return title in {'1','3','4','5'}
|
||||||
|
with ExitStack() as stack:
|
||||||
|
for name, value in [('get_runtime_settings', runtime), ('_recent_cache_stale', False),
|
||||||
|
('active_repair_request_ids', {'5'}), ('get_request_cache_payload', None)]:
|
||||||
|
stack.enter_context(patch.object(requests, name, return_value=value))
|
||||||
|
stack.enter_context(patch.dict(requests._recent_cache, {'items':rows}))
|
||||||
|
stack.enter_context(patch.object(requests, '_request_is_available_in_jellyfin', new=available))
|
||||||
|
user={'role':'user','username':'viewer','jellyseerr_user_id':10}
|
||||||
|
expected={'working':[2,5], 'ready':[1,3], 'partial':[4], 'approved':[6],
|
||||||
|
'pending':[7], 'declined':[8], 'in_progress':[2,4,5,6]}
|
||||||
|
for stage, ids in expected.items():
|
||||||
|
result=await requests.recent_requests(take=20,skip=0,days=0,stage=stage,user=user)
|
||||||
|
self.assertEqual([r['id'] for r in result['results']],ids,stage)
|
||||||
|
result=await requests.recent_requests(take=1,skip=1,days=0,stage='working',user=user)
|
||||||
|
self.assertEqual([r['id'] for r in result['results']],[5])
|
||||||
|
result=await requests.recent_requests(take=1,skip=0,days=0,stage='ready',user=user)
|
||||||
|
self.assertEqual(result['results'][0]['status'],4)
|
||||||
|
result=await requests.recent_requests(take=20,skip=0,days=0,stage='all',user={**user,'jellyseerr_user_id':99})
|
||||||
|
self.assertEqual(result['results'],[])
|
||||||
@@ -85,6 +85,7 @@ export default function HomePage() {
|
|||||||
router.push('/login')
|
router.push('/login')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let cancelled = false
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
setRecentLoading(true)
|
setRecentLoading(true)
|
||||||
setRecentError(null)
|
setRecentError(null)
|
||||||
@@ -100,6 +101,7 @@ export default function HomePage() {
|
|||||||
throw new Error(`Auth failed: ${meResponse.status}`)
|
throw new Error(`Auth failed: ${meResponse.status}`)
|
||||||
}
|
}
|
||||||
const me = await meResponse.json()
|
const me = await meResponse.json()
|
||||||
|
if (cancelled) return
|
||||||
const userRole = me?.role ?? null
|
const userRole = me?.role ?? null
|
||||||
setRole(userRole)
|
setRole(userRole)
|
||||||
setAuthReady(true)
|
setAuthReady(true)
|
||||||
@@ -121,18 +123,20 @@ export default function HomePage() {
|
|||||||
throw new Error(`Recent requests failed: ${response.status}`)
|
throw new Error(`Recent requests failed: ${response.status}`)
|
||||||
}
|
}
|
||||||
const data = await response.json()
|
const data = await response.json()
|
||||||
|
if (cancelled) return
|
||||||
if (Array.isArray(data?.results)) {
|
if (Array.isArray(data?.results)) {
|
||||||
setRecent(normalizeRecentResults(data.results))
|
setRecent(normalizeRecentResults(data.results))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
setRecentError('Recent requests are not available right now.')
|
if (!cancelled) setRecentError('Recent requests are not available right now.')
|
||||||
} finally {
|
} finally {
|
||||||
setRecentLoading(false)
|
if (!cancelled) setRecentLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
load()
|
void load()
|
||||||
|
return () => { cancelled = true }
|
||||||
}, [recentDays, recentStage])
|
}, [recentDays, recentStage])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user