Upload stats

This commit is contained in:
miruka 2019-12-05 10:00:23 -04:00
parent 3aff20006c
commit a555ad0e19
4 changed files with 57 additions and 25 deletions

View File

@ -215,8 +215,8 @@ class MatrixClient(nio.AsyncClient):
try: try:
await self._send_file(item_uuid, room_id, path) await self._send_file(item_uuid, room_id, path)
except asyncio.CancelledError: except (nio.TransferCancelledError, asyncio.CancelledError):
del self.models[Upload, room_id][item_uuid] del self.models[Upload, room_id][str(item_uuid)]
async def _send_file( async def _send_file(
@ -235,18 +235,27 @@ class MatrixClient(nio.AsyncClient):
task = asyncio.Task.current_task() task = asyncio.Task.current_task()
upload_item = Upload(item_uuid, task, path, total_size=size) upload_item = Upload(item_uuid, task, path, total_size=size)
self.models[Upload, room_id][upload_item.uuid] = upload_item self.models[Upload, room_id][str(item_uuid)] = upload_item
def on_transfered(transfered: int) -> None:
upload_item.uploaded = transfered
upload_item.time_left = monitor.remaining_time
def on_speed_change(speed: float) -> None:
upload_item.speed = speed
monitor = nio.TransferMonitor(size, on_transfered, on_speed_change)
try: try:
url, mime, crypt_dict = await self.upload( url, mime, crypt_dict = await self.upload(
path, filename=path.name, encrypt=encrypt, path, filename=path.name, encrypt=encrypt, monitor=monitor,
) )
except (MatrixError, OSError) as err: except (MatrixError, OSError) as err:
upload_item.status = UploadStatus.Error upload_item.status = UploadStatus.Error
upload_item.error = type(err) upload_item.error = type(err)
upload_item.error_args = err.args upload_item.error_args = err.args
# Wait for cancellation, see parent send_file() method # Wait for cancellation from UI, see parent send_file() method
while True: while True:
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
@ -606,9 +615,10 @@ class MatrixClient(nio.AsyncClient):
async def upload( async def upload(
self, self,
data: UploadData, data: UploadData,
mime: Optional[str] = None, mime: Optional[str] = None,
filename: Optional[str] = None, filename: Optional[str] = None,
encrypt: bool = False, encrypt: bool = False,
monitor: Optional[nio.TransferMonitor] = None,
) -> UploadReturn: ) -> UploadReturn:
mime = mime or await utils.guess_mime(data) mime = mime or await utils.guess_mime(data)
@ -618,6 +628,7 @@ class MatrixClient(nio.AsyncClient):
"application/octet-stream" if encrypt else mime, "application/octet-stream" if encrypt else mime,
filename, filename,
encrypt, encrypt,
monitor,
) )
if isinstance(response, nio.UploadError): if isinstance(response, nio.UploadError):

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
import re import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Type, Union from typing import Any, Dict, List, Optional, Tuple, Type, Union
from uuid import UUID from uuid import UUID
@ -114,12 +114,16 @@ class UploadStatus(AutoStrEnum):
@dataclass @dataclass
class Upload(ModelItem): class Upload(ModelItem):
uuid: UUID = field() uuid: UUID = field()
task: asyncio.Task = field() task: asyncio.Task = field()
filepath: Path = field() filepath: Path = field()
total_size: int = 0
uploaded: int = 0
speed: float = 0
time_left: Optional[timedelta] = None
status: UploadStatus = UploadStatus.Uploading status: UploadStatus = UploadStatus.Uploading
total_size: int = 0
uploaded: int = 0
error: OptionalExceptionType = type(None) error: OptionalExceptionType = type(None)
error_args: Tuple[Any, ...] = () error_args: Tuple[Any, ...] = ()

View File

@ -7,6 +7,7 @@ import inspect
import io import io
import logging as log import logging as log
import xml.etree.cElementTree as xml_etree # FIXME: bandit warning import xml.etree.cElementTree as xml_etree # FIXME: bandit warning
from datetime import timedelta
from enum import Enum from enum import Enum
from enum import auto as autostr from enum import auto as autostr
from pathlib import Path from pathlib import Path
@ -140,6 +141,9 @@ def serialize_value_for_qml(value: Any) -> Any:
if isinstance(value, UUID): if isinstance(value, UUID):
return str(value) return str(value)
if isinstance(value, timedelta):
return value.total_seconds() * 1000
if inspect.isclass(value): if inspect.isclass(value):
return value.__name__ return value.__name__

View File

@ -94,7 +94,7 @@ Rectangle {
.arg(fileName) : .arg(fileName) :
model.error === "IsADirectoryError" ? model.error === "IsADirectoryError" ?
qsTr("Can't upload folders: %1") qsTr("Can't upload folders, need a file: %1")
.arg(filePath) : .arg(filePath) :
model.error === "FileNotFoundError" ? model.error === "FileNotFoundError" ?
@ -128,6 +128,13 @@ Rectangle {
readonly property string filePath: readonly property string filePath:
model.filepath.replace(/^file:\/\//, "") model.filepath.replace(/^file:\/\//, "")
HoverHandler { id: statusLabelHover }
HToolTip {
text: parent.truncated ? parent.text : ""
visible: text && statusLabelHover.hovered
}
} }
HSpacer {} HSpacer {}
@ -135,7 +142,11 @@ Rectangle {
HLabel { HLabel {
id: uploadCountLabel id: uploadCountLabel
visible: Layout.preferredWidth > 0 visible: Layout.preferredWidth > 0
text: qsTr("%1") text: qsTr("-%1 %2/s %3/%4")
.arg(model.time_left ?
Utils.formatDuration(msLeft) : "∞")
.arg(CppUtils.formattedBytes(model.speed))
.arg(CppUtils.formattedBytes(uploaded))
.arg(CppUtils.formattedBytes(model.total_size)) .arg(CppUtils.formattedBytes(model.total_size))
topPadding: theme.spacing / 2 topPadding: theme.spacing / 2
@ -146,6 +157,12 @@ Rectangle {
Layout.preferredWidth: Layout.preferredWidth:
model.status === "Uploading" ? implicitWidth : 0 model.status === "Uploading" ? implicitWidth : 0
property int msLeft: model.time_left || -1
property int uploaded: model.uploaded
Behavior on msLeft { HNumberAnimation { duration: 1000 } }
Behavior on uploaded { HNumberAnimation { duration: 1000 }}
Behavior on Layout.preferredWidth { HNumberAnimation {} } Behavior on Layout.preferredWidth { HNumberAnimation {} }
} }
@ -153,20 +170,15 @@ Rectangle {
onTapped: if (model.status !== "Error") onTapped: if (model.status !== "Error")
statusLabel.expand = ! statusLabel.expand statusLabel.expand = ! statusLabel.expand
} }
HoverHandler { id: infoRowHover }
HToolTip {
id: statusToolTip
text: statusLabel.truncated ? statusLabel.text : ""
visible: text && infoRowHover.hovered
}
} }
HProgressBar { HProgressBar {
id: progressBar id: progressBar
visible: Layout.maximumHeight !== 0 visible: Layout.maximumHeight !== 0
indeterminate: true indeterminate: model.status !== "Uploading"
value: model.uploaded
to: model.total_size
foregroundColor: foregroundColor:
model.status === "Error" ? model.status === "Error" ?
theme.controls.progressBar.errorForeground : theme.controls.progressBar.errorForeground :
@ -176,6 +188,7 @@ Rectangle {
Layout.maximumHeight: Layout.maximumHeight:
model.status === "Error" && indeterminate ? 0 : -1 model.status === "Error" && indeterminate ? 0 : -1
Behavior on value { HNumberAnimation { duration: 1000 } }
Behavior on Layout.maximumHeight { HNumberAnimation {} } Behavior on Layout.maximumHeight { HNumberAnimation {} }
} }
} }