From 7ac3fbea2b9a2dde65e2cdde3fae872237013ce0 Mon Sep 17 00:00:00 2001 From: Roland Date: Fri, 1 Apr 2022 16:45:59 -0600 Subject: [PATCH] Creating ClearFailedDownloads button. --- app/src/api.js | 18 ++++++++++++-- app/src/components/Admin.js | 48 +++++++++++++++++++++++++++++++++--- wrolpi/downloader.py | 6 +++++ wrolpi/root_api.py | 7 ++++++ wrolpi/test/test_root_api.py | 20 +++++++++++++-- 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/app/src/api.js b/app/src/api.js index e4790a10..444f4110 100644 --- a/app/src/api.js +++ b/app/src/api.js @@ -532,7 +532,7 @@ export async function importMapFiles(paths) { } } -export async function clearDownloads() { +export async function clearCompletedDownloads() { let response = await apiPost(`${API_URI}/download/clear_completed`); if (response.status === 204) { return null @@ -540,7 +540,21 @@ export async function clearDownloads() { toast({ type: 'error', title: 'Error!', - description: 'Could not clear downloads! See server logs.', + description: 'Could not clear completed downloads! See server logs.', + time: 5000, + }); + } +} + +export async function clearFailedDownloads() { + let response = await apiPost(`${API_URI}/download/clear_failed`); + if (response.status === 204) { + return null + } else { + toast({ + type: 'error', + title: 'Error!', + description: 'Could not clear failed downloads! See server logs.', time: 5000, }); } diff --git a/app/src/components/Admin.js b/app/src/components/Admin.js index 9b3b7163..864b4c2b 100644 --- a/app/src/components/Admin.js +++ b/app/src/components/Admin.js @@ -11,7 +11,15 @@ import { Placeholder, Table } from "semantic-ui-react"; -import {clearDownloads, getDownloads, getSettings, killDownload, postDownload, saveSettings} from "../api"; +import { + clearCompletedDownloads, + clearFailedDownloads, + getDownloads, + getSettings, + killDownload, + postDownload, + saveSettings +} from "../api"; import TimezoneSelect from 'react-timezone-select'; import { DisableDownloadsToggle, @@ -203,8 +211,11 @@ function ClearCompleteDownloads() { async function localClearDownloads() { setDisabled(true); - await clearDownloads(); - setDisabled(false); + try { + await clearCompletedDownloads(); + } finally { + setDisabled(false); + } } return <> @@ -216,6 +227,36 @@ function ClearCompleteDownloads() { } +function ClearFailedDownloads() { + const [open, setOpen] = React.useState(false); + const [disabled, setDisabled] = React.useState(false); + + async function localDeleteFailed() { + setDisabled(true); + try { + await clearFailedDownloads(); + } finally { + setDisabled(false); + setOpen(false); + } + } + + return <> + + setOpen(false)} + onConfirm={localDeleteFailed} + /> + +} + class DownloadRow extends React.Component { render() { @@ -408,6 +449,7 @@ class Downloads extends React.Component { onceTable = ( <> + {stoppableHeader} diff --git a/wrolpi/downloader.py b/wrolpi/downloader.py index a6cce697..c1c03b02 100644 --- a/wrolpi/downloader.py +++ b/wrolpi/downloader.py @@ -693,6 +693,12 @@ class DownloadManager: session.query(Download).filter(Download.status == 'complete').delete() session.commit() + @optional_session + def delete_failed(self, session: Session): + """Delete any failed download records.""" + session.query(Download).filter(Download.status == 'failed').delete() + session.commit() + # The global DownloadManager. This should be used everywhere! download_manager = DownloadManager() diff --git a/wrolpi/root_api.py b/wrolpi/root_api.py index cac684e0..77e93f71 100644 --- a/wrolpi/root_api.py +++ b/wrolpi/root_api.py @@ -235,6 +235,13 @@ async def clear_completed(_: Request): return response.empty() +@root_api.post('/download/clear_failed') +@openapi.description('Clear failed downloads') +async def clear_failed(_: Request): + download_manager.delete_failed() + return response.empty() + + @root_api.delete('/download/') @openapi.description('Delete a download') @wrol_mode_check diff --git a/wrolpi/test/test_root_api.py b/wrolpi/test/test_root_api.py index f987fb8c..7f80c8b1 100644 --- a/wrolpi/test/test_root_api.py +++ b/wrolpi/test/test_root_api.py @@ -202,7 +202,8 @@ def test_hotspot_settings(test_session, test_client, test_config): def test_throttle_toggle(test_session, test_client, test_config): - with mock.patch('wrolpi.admin.subprocess') as mock_subprocess: + with mock.patch('wrolpi.admin.subprocess') as mock_subprocess, \ + mock.patch('wrolpi.admin.CPUFREQ_INFO', "this value isn't even used"): mock_subprocess.check_output.side_effect = [ b'wlan0: unavailable', b'The governor "ondemand" may decide ', @@ -221,7 +222,8 @@ def test_clear_downloads(test_session, test_client, test_config): d2 = Download(url='https://example.com/2', status='pending') d3 = Download(url='https://example.com/3', status='deferred') d4 = Download(url='https://example.com/4', status='new') - session.add_all([d1, d2, d3, d4]) + d5 = Download(url='https://example.com/5', status='failed') + session.add_all([d1, d2, d3, d4, d5]) def check_download(download_, expected_): assert download_['url'] == expected_['url'] @@ -230,6 +232,7 @@ def test_clear_downloads(test_session, test_client, test_config): request, response = api_app.test_client.get('/api/download') expected = [ {'url': 'https://example.com/2', 'status': 'pending'}, + {'url': 'https://example.com/5', 'status': 'failed'}, {'url': 'https://example.com/4', 'status': 'new'}, {'url': 'https://example.com/3', 'status': 'deferred'}, {'url': 'https://example.com/1', 'status': 'complete'}, @@ -240,6 +243,19 @@ def test_clear_downloads(test_session, test_client, test_config): request, response = api_app.test_client.post('/api/download/clear_completed') assert response.status_code == HTTPStatus.NO_CONTENT + request, response = api_app.test_client.get('/api/download') + expected = [ + {'url': 'https://example.com/2', 'status': 'pending'}, + {'url': 'https://example.com/5', 'status': 'failed'}, + {'url': 'https://example.com/4', 'status': 'new'}, + {'url': 'https://example.com/3', 'status': 'deferred'}, + ] + for download, expected in zip_longest(response.json['once_downloads'], expected): + check_download(download, expected) + + request, response = api_app.test_client.post('/api/download/clear_failed') + assert response.status_code == HTTPStatus.NO_CONTENT + request, response = api_app.test_client.get('/api/download') expected = [ {'url': 'https://example.com/2', 'status': 'pending'},