Creating ClearFailedDownloads button.

This commit is contained in:
Roland 2022-04-01 16:45:59 -06:00
parent c4f511e469
commit 7ac3fbea2b
No known key found for this signature in database
GPG key ID: 9D6DBD3ACF11AB93
5 changed files with 92 additions and 7 deletions

View file

@ -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,
});
}

View file

@ -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 <>
<Button
onClick={() => setOpen(true)}
disabled={disabled}
color='red'
>Clear Failed</Button>
<Confirm
open={open}
content='Are you sure you want to delete failed downloads? They will not be retried.'
confirmButton='Delete'
onCancel={() => setOpen(false)}
onConfirm={localDeleteFailed}
/>
</>
}
class DownloadRow extends React.Component {
render() {
@ -408,6 +449,7 @@ class Downloads extends React.Component {
onceTable = (
<>
<ClearCompleteDownloads/>
<ClearFailedDownloads/>
<Table>
{stoppableHeader}
<Table.Body>

View file

@ -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()

View file

@ -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/<download_id:integer>')
@openapi.description('Delete a download')
@wrol_mode_check

View file

@ -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'},