def delete_file() -> str: filename = str(request.args.get("filename")) path = (config.get_files_directory() / filename).resolve() if config.get_files_directory() not in path.parents: abort(400) # we use os.path.isfile instead of Path.is_file here because pyfakefs doesn't # seem to properly mock Path.is_file as of pyfakefs 4.4.0 if not os.path.isfile(path): abort(400) os.remove(path) return jsonify({"success": True})
def list_files() -> str: path_parameter = str(request.args.get("path", ".")) path = (config.get_files_directory() / path_parameter).resolve() if ( config.get_files_directory() not in path.parents and path != config.get_files_directory() ): abort(400) with os.scandir(path) as dir_entries: files = [] directories = [] for dir_entry in sorted( dir_entries, key=lambda t: t.stat().st_mtime, reverse=True ): if dir_entry.is_file(): sliced_model_file: Optional[SlicedModelFile] = None if dir_entry.name.endswith(tuple(get_supported_extensions())): sliced_model_file = read_cached_sliced_model_file( path / dir_entry.name ) file_data: Dict[str, Any] = { "filename": dir_entry.name, "path": str( (path / dir_entry.name).relative_to( config.get_files_directory() ) ), } if sliced_model_file: file_data = { "print_time_secs": sliced_model_file.print_time_secs, "can_be_printed": True, **file_data, } else: file_data = { "can_be_printed": False, **file_data, } files.append(file_data) else: directories.append({"dirname": dir_entry.name}) return jsonify( { "directories": directories, "files": files, } )
def file_preview() -> Response: filename = str(request.args.get("filename")) path = (config.get_files_directory() / filename).resolve() if config.get_files_directory() not in path.parents: abort(400) preview_bytes = read_cached_preview(path) response = make_response(preview_bytes) response.headers.set("Content-Type", "image/png") response.headers.set( "Content-Disposition", "attachment", filename=f"{filename}.png" ) return response
def read_cached_preview(filename: str) -> bytes: assert os.path.isabs(filename) bytes = io.BytesIO() file_format = get_file_format(filename) preview_image: png.Image = file_format.read_preview( config.get_files_directory() / filename) preview_image.write(bytes) return bytes.getvalue()
def test_can_customize_files_directory(self) -> None: self.fs.create_file( "/etc/mariner/config.toml", contents=""" files_directory = "/var/mariner" """, ) expect(config.get_files_directory()).to_equal(Path("/var/mariner"))
def test_delete_file(self) -> None: expect(os.path.exists(config.get_files_directory() / "mariner.ctb")).to_equal( False ) self.fs.create_file( "/mnt/usb_share/mariner.ctb", contents=self.ctb_file_contents ) expect(os.path.exists(config.get_files_directory() / "mariner.ctb")).to_equal( True ) response = self.client.post("/api/delete_file?filename=mariner.ctb") expect(response.status_code).to_equal(200) expect(response.get_json()).to_equal({"success": True}) expect(os.path.exists(config.get_files_directory() / "mariner.ctb")).to_equal( False )
def file_details() -> str: filename = str(request.args.get("filename")) path = (config.get_files_directory() / filename).resolve() if config.get_files_directory() not in path.parents: abort(400) sliced_model_file = read_cached_sliced_model_file(path) return jsonify( { "filename": sliced_model_file.filename, "path": filename, "bed_size_mm": list(sliced_model_file.bed_size_mm), "height_mm": round(sliced_model_file.height_mm, 4), "layer_count": sliced_model_file.layer_count, "layer_height_mm": round(sliced_model_file.layer_height_mm, 4), "resolution": list(sliced_model_file.resolution), "print_time_secs": sliced_model_file.print_time_secs, } )
def test_upload_file_with_sanitized_file(self) -> None: data = {"file": (io.BytesIO(b"abcdef"), "../../../etc/passwd.ctb")} with patch.object(FileStorage, "save") as save_file_mock: response = self.client.post("/api/upload_file", data=data) expect(response.status_code).to_equal(200) expect(response.get_json()).to_equal({"success": True}) save_file_mock.assert_called_once_with( str(config.get_files_directory() / "etc_passwd.ctb") )
def test_upload_file_with_upper_case_extension(self) -> None: data = {"file": (io.BytesIO(b"abcdef"), "myfile.CtB")} with patch.object(FileStorage, "save") as save_file_mock: response = self.client.post("/api/upload_file", data=data) expect(response.status_code).to_equal(200) expect(response.get_json()).to_equal({"success": True}) save_file_mock.assert_called_once_with( str(config.get_files_directory() / "myfile.CtB") )
def run(self) -> None: os.nice(5) globs = [ config.get_files_directory().rglob(f"*{extension}") for extension in get_supported_extensions() ] for file in chain.from_iterable(globs): read_cached_sliced_model_file(file.absolute()) read_cached_preview(file.absolute())
def upload_file() -> str: file = request.files.get("file") if file is None or file.filename == "": abort(400) if os.path.splitext(file.filename)[1] not in get_supported_extensions(): abort(400) filename = secure_filename(file.filename) file.save(str(config.get_files_directory() / filename)) os.sync() return jsonify({"success": True})
def test_default_values(self) -> None: expect(config.get_files_directory()).to_equal(Path("/mnt/usb_share")) expect(config.get_printer_display_name()).to_equal(None) expect(config.get_printer_serial_port()).to_equal("/dev/serial0") expect(config.get_printer_baudrate()).to_equal(115200) expect(config.get_http_host()).to_equal("0.0.0.0") expect(config.get_http_port()).to_equal(5050) expect(config.get_cache_directory()).to_equal("/tmp/mariner/")
def print_status() -> str: with ChiTuPrinter() as printer: # the printer sends periodic "ok" responses over serial. this means that # sometimes we get an unexpected response from the printer (an "ok" instead of # the print status we expected). due to this, we retry at most 3 times here # until we have a successful response. see issue #180 selected_file = retry( printer.get_selected_file, UnexpectedPrinterResponse, num_retries=3, ) print_status = retry( printer.get_print_status, UnexpectedPrinterResponse, num_retries=3, ) if print_status.state == PrinterState.IDLE: progress = 0.0 print_details = {} else: sliced_model_file = read_cached_sliced_model_file( config.get_files_directory() / selected_file) if print_status.current_byte == 0: current_layer = 1 else: current_layer = ( sliced_model_file.end_byte_offset_by_layer.index( print_status.current_byte) + 1) progress = (100.0 * none_throws(current_layer - 1) / none_throws(sliced_model_file.layer_count)) print_details = { "current_layer": current_layer, "layer_count": sliced_model_file.layer_count, "print_time_secs": sliced_model_file.print_time_secs, "time_left_secs": round(sliced_model_file.print_time_secs * (100.0 - progress) / 100.0), } return jsonify({ "state": print_status.state.value, "selected_file": selected_file, "progress": progress, **print_details, })
def print_status() -> str: with ElegooMars() as elegoo_mars: selected_file = elegoo_mars.get_selected_file() print_status = elegoo_mars.get_print_status() if print_status.state == PrinterState.IDLE: progress = 0.0 print_details = {} else: sliced_model_file = read_cached_sliced_model_file( config.get_files_directory() / selected_file ) if print_status.current_byte == 0: current_layer = 1 else: current_layer = ( sliced_model_file.end_byte_offset_by_layer.index( print_status.current_byte ) + 1 ) progress = ( 100.0 * none_throws(current_layer - 1) / none_throws(sliced_model_file.layer_count) ) print_details = { "current_layer": current_layer, "layer_count": sliced_model_file.layer_count, "print_time_secs": sliced_model_file.print_time_secs, "time_left_secs": round( sliced_model_file.print_time_secs * (100.0 - progress) / 100.0 ), } return jsonify( { "state": print_status.state.value, "selected_file": selected_file, "progress": progress, **print_details, } )
def read_cached_sliced_model_file(filename: str) -> SlicedModelFile: assert os.path.isabs(filename) file_format = get_file_format(filename) return file_format.read(config.get_files_directory() / filename)