From c4f511e469e389640dc64714639d1b73cd6a828c Mon Sep 17 00:00:00 2001 From: Roland Date: Fri, 1 Apr 2022 16:31:49 -0600 Subject: [PATCH] Creating ClearCompleteDownloads button. --- app/src/api.js | 14 +++++++++++++ app/src/components/Admin.js | 40 ++++++++++++++++++++++++++++-------- wrolpi/admin.py | 6 ++++++ wrolpi/downloader.py | 6 ++++++ wrolpi/root_api.py | 7 +++++++ wrolpi/test/test_root_api.py | 35 +++++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 9 deletions(-) diff --git a/app/src/api.js b/app/src/api.js index 611cc71b..e4790a10 100644 --- a/app/src/api.js +++ b/app/src/api.js @@ -531,3 +531,17 @@ export async function importMapFiles(paths) { }); } } + +export async function clearDownloads() { + let response = await apiPost(`${API_URI}/download/clear_completed`); + if (response.status === 204) { + return null + } else { + toast({ + type: 'error', + title: 'Error!', + description: 'Could not clear downloads! See server logs.', + time: 5000, + }); + } +} diff --git a/app/src/components/Admin.js b/app/src/components/Admin.js index 2aa52fbc..9b3b7163 100644 --- a/app/src/components/Admin.js +++ b/app/src/components/Admin.js @@ -11,7 +11,7 @@ import { Placeholder, Table } from "semantic-ui-react"; -import {getDownloads, getSettings, killDownload, postDownload, saveSettings} from "../api"; +import {clearDownloads, getDownloads, getSettings, killDownload, postDownload, saveSettings} from "../api"; import TimezoneSelect from 'react-timezone-select'; import { DisableDownloadsToggle, @@ -198,6 +198,24 @@ class WROLMode extends React.Component { } } +function ClearCompleteDownloads() { + const [disabled, setDisabled] = React.useState(false); + + async function localClearDownloads() { + setDisabled(true); + await clearDownloads(); + setDisabled(false); + } + + return <> + + +} + class DownloadRow extends React.Component { render() { @@ -387,14 +405,18 @@ class Downloads extends React.Component { if (this.state.once_downloads !== null && this.state.once_downloads.length === 0) { onceTable =

No downloads are scheduled to be downloaded.

} else if (this.state.once_downloads !== null) { - onceTable = ( - {stoppableHeader} - - {this.state.once_downloads.map((i) => - - )} - -
); + onceTable = ( + <> + + + {stoppableHeader} + + {this.state.once_downloads.map((i) => + + )} + +
+ ); } let recurringTable = tablePlaceholder; diff --git a/wrolpi/admin.py b/wrolpi/admin.py index cd857468..cbff952a 100644 --- a/wrolpi/admin.py +++ b/wrolpi/admin.py @@ -23,6 +23,9 @@ class HotspotStatus(enum.Enum): def hotspot_status() -> HotspotStatus: + if NMCLI is None: + return HotspotStatus.unknown + cmd = (NMCLI,) try: output = subprocess.check_output(cmd).decode().strip() @@ -97,6 +100,9 @@ GOVERNOR_MAP = { def throttle_status() -> GovernorStatus: + if not CPUFREQ_INFO: + return GovernorStatus.unknown + cmd = (CPUFREQ_INFO,) try: output = subprocess.check_output(cmd) diff --git a/wrolpi/downloader.py b/wrolpi/downloader.py index e58cdeaf..a6cce697 100644 --- a/wrolpi/downloader.py +++ b/wrolpi/downloader.py @@ -687,6 +687,12 @@ class DownloadManager: next_download = local_timezone(next_download) return next_download + @optional_session + def delete_completed(self, session: Session): + """Delete any completed download records.""" + session.query(Download).filter(Download.status == 'complete').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 a898cb94..cac684e0 100644 --- a/wrolpi/root_api.py +++ b/wrolpi/root_api.py @@ -228,6 +228,13 @@ async def enable_downloads(_: Request): return response.empty() +@root_api.post('/download/clear_completed') +@openapi.description('Clear completed downloads') +async def clear_completed(_: Request): + download_manager.delete_completed() + 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 144dba72..f987fb8c 100644 --- a/wrolpi/test/test_root_api.py +++ b/wrolpi/test/test_root_api.py @@ -213,3 +213,38 @@ def test_throttle_toggle(test_session, test_client, test_config): assert response.status_code == HTTPStatus.OK assert response.json['throttle_on_startup'] is False assert response.json['throttle_status'] == 'ondemand' + + +def test_clear_downloads(test_session, test_client, test_config): + with get_db_session(commit=True) as session: + d1 = Download(url='https://example.com/1', status='complete') + 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]) + + def check_download(download_, expected_): + assert download_['url'] == expected_['url'] + assert download_['status'] == expected_['status'] + + request, response = api_app.test_client.get('/api/download') + expected = [ + {'url': 'https://example.com/2', 'status': 'pending'}, + {'url': 'https://example.com/4', 'status': 'new'}, + {'url': 'https://example.com/3', 'status': 'deferred'}, + {'url': 'https://example.com/1', 'status': 'complete'}, + ] + 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_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/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)