Improve ftp error logging. Update ftp tests.

This commit is contained in:
Anton Skrypnyk 2024-07-26 16:53:20 +03:00
parent 0d16732561
commit 4ea98036e5
3 changed files with 87 additions and 40 deletions

View File

@ -26,6 +26,7 @@ wrapper for FTPS server interactions
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone
import ftplib import ftplib
import os import os
from pathlib import Path from pathlib import Path
@ -194,6 +195,28 @@ class IoTFTPSConnection:
pass pass
return return
def get_file_size(self, file_path: str):
try:
return self.ftps_session.size(file_path)
except Exception as e:
raise RuntimeError(
f'Cannot get file size for "{file_path}" due to error: {str(e)}'
)
def get_file_date(self, file_path: str) -> datetime:
try:
date_response = self.ftps_session.sendcmd(f"MDTM {file_path}").replace(
"213 ", ""
)
date = datetime.strptime(date_response, "%Y%m%d%H%M%S").replace(
tzinfo=timezone.utc
)
return date
except Exception as e:
raise RuntimeError(
f'Cannot get file date for "{file_path}" due to error: {str(e)}'
)
@dataclass @dataclass
class IoTFTPSClient: class IoTFTPSClient:

View File

@ -102,13 +102,8 @@ class RemoteSDCardFileList:
file_path: Path, file_path: Path,
existing_files: list[str] | None = None, existing_files: list[str] | None = None,
): ):
file_size = ftp.ftps_session.size(file_path.as_posix()) file_size = ftp.get_file_size(file_path.as_posix())
date_str = ftp.ftps_session.sendcmd(f"MDTM {file_path.as_posix()}").replace( date = ftp.get_file_date(file_path.as_posix())
"213 ", ""
)
date = datetime.datetime.strptime(date_str, "%Y%m%d%H%M%S").replace(
tzinfo=datetime.timezone.utc
)
file_name = file_path.name.lower() file_name = file_path.name.lower()
dosname = get_dos_filename(file_name, existing_filenames=existing_files).lower() dosname = get_dos_filename(file_name, existing_filenames=existing_files).lower()
return FileInfo( return FileInfo(
@ -128,10 +123,13 @@ class RemoteSDCardFileList:
existing_files = [] existing_files = []
for entry in files: for entry in files:
file_info = self._get_ftp_file_info(ftp, entry, existing_files) try:
yield file_info file_info = self._get_ftp_file_info(ftp, entry, existing_files)
existing_files.append(file_info.file_name) yield file_info
existing_files.append(file_info.dosname) existing_files.append(file_info.file_name)
existing_files.append(file_info.dosname)
except Exception as e:
self._logger.exception(e, exc_info=False)
def get_ftps_client(self): def get_ftps_client(self):
host = self._settings.get(["host"]) host = self._settings.get(["host"])

View File

@ -10,6 +10,9 @@ import pybambu.commands
from octoprint_bambu_printer.printer.bambu_virtual_printer import BambuVirtualPrinter from octoprint_bambu_printer.printer.bambu_virtual_printer import BambuVirtualPrinter
from octoprint_bambu_printer.printer.file_system.file_info import FileInfo from octoprint_bambu_printer.printer.file_system.file_info import FileInfo
from octoprint_bambu_printer.printer.file_system.ftps_client import IoTFTPSClient from octoprint_bambu_printer.printer.file_system.ftps_client import IoTFTPSClient
from octoprint_bambu_printer.printer.file_system.remote_sd_card_file_list import (
RemoteSDCardFileList,
)
from octoprint_bambu_printer.printer.states.idle_state import IdleState from octoprint_bambu_printer.printer.states.idle_state import IdleState
from octoprint_bambu_printer.printer.states.paused_state import PausedState from octoprint_bambu_printer.printer.states.paused_state import PausedState
from octoprint_bambu_printer.printer.states.printing_state import PrintingState from octoprint_bambu_printer.printer.states.printing_state import PrintingState
@ -29,13 +32,14 @@ def log_test():
class DictGetter: class DictGetter:
def __init__(self, options: dict) -> None: def __init__(self, options: dict, default_value=None) -> None:
self._options: dict[str | tuple[str, ...], Any] = options self.options: dict[str | tuple[str, ...], Any] = options
self._default_value = default_value
def __call__(self, key: str | list[str] | tuple[str, ...]): def __call__(self, key: str | list[str] | tuple[str, ...]):
if isinstance(key, list): if isinstance(key, list):
key = tuple(key) key = tuple(key)
return self._options.get(key, None) return self.options.get(key, self._default_value)
@fixture @fixture
@ -186,6 +190,54 @@ def test_list_sd_card(printer: BambuVirtualPrinter):
assert result[4] == b"ok" assert result[4] == b"ok"
def test_list_ftp_paths_bambu_p1(settings, ftps_session_mock):
settings.get.side_effect.options[("device_type",)] = "P1S"
file_system = RemoteSDCardFileList(settings)
timelapse_files = ["timelapse/video.avi", "timelapse/video2.avi"]
ftps_session_mock.size.side_effect = DictGetter(
{file: 100 for file in timelapse_files}
)
ftps_session_mock.sendcmd.side_effect = DictGetter(
{
f"MDTM {file}": _ftp_date_format(datetime(2024, 5, 7))
for file in timelapse_files
}
)
ftps_session_mock.nlst.side_effect = DictGetter(
{"timelapse/": [Path(f).name for f in timelapse_files]}
)
timelapse_paths = list(map(Path, timelapse_files))
result_files = file_system.get_all_timelapse_files()
assert len(timelapse_files) == len(result_files) and all(
file_info.path in timelapse_paths for file_info in result_files
)
def test_list_ftp_paths_bambu_x1(settings, ftps_session_mock):
settings.get.side_effect.options[("device_type",)] = "X1"
file_system = RemoteSDCardFileList(settings)
timelapse_files = ["timelapse/video.mp4", "timelapse/video2.mp4"]
ftps_session_mock.size.side_effect = DictGetter(
{file: 100 for file in timelapse_files}
)
ftps_session_mock.sendcmd.side_effect = DictGetter(
{
f"MDTM {file}": _ftp_date_format(datetime(2024, 5, 7))
for file in timelapse_files
}
)
ftps_session_mock.nlst.side_effect = DictGetter({"timelapse/": timelapse_files})
timelapse_paths = list(map(Path, timelapse_files))
result_files = file_system.get_all_timelapse_files()
assert len(timelapse_files) == len(result_files) and all(
file_info.path in timelapse_paths for file_info in result_files
)
def test_cannot_start_print_without_file(printer: BambuVirtualPrinter): def test_cannot_start_print_without_file(printer: BambuVirtualPrinter):
printer.write(b"M24\n") printer.write(b"M24\n")
printer.flush() printer.flush()
@ -360,29 +412,3 @@ def test_finished_print_job_reset_after_new_file_selected(
assert printer.current_print_job is not None assert printer.current_print_job is not None
assert printer.current_print_job.file_info.file_name == "print2.3mf" assert printer.current_print_job.file_info.file_name == "print2.3mf"
assert printer.current_print_job.progress == 0 assert printer.current_print_job.progress == 0
def test_timelapse_paths_bambu_x1(printer: BambuVirtualPrinter, ftps_session_mock):
timelapse_files = ["timelapse/video.mp4", "timelapse/video2.avi"]
ftps_session_mock.size.side_effect = DictGetter(
{file: 100 for file in timelapse_files}
)
ftps_session_mock.sendcmd.side_effect = DictGetter(
{
f"MDTM {file}": _ftp_date_format(datetime(2024, 5, 7))
for file in timelapse_files
}
)
ftps_session_mock.nlst.side_effect = DictGetter(
{
"": ["timelapse"],
"timelapse/": timelapse_files,
"cache/": [],
}
)
timelapse_paths = list(map(Path, timelapse_files))
assert all(
file_info.path in timelapse_paths
for file_info in printer.file_system.get_all_timelapse_files()
)