Esempio n. 1
0
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})
Esempio n. 2
0
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,
            }
        )
Esempio n. 3
0
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
Esempio n. 4
0
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()
Esempio n. 5
0
    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"))
Esempio n. 6
0
    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
        )
Esempio n. 7
0
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,
        }
    )
Esempio n. 8
0
 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")
     )
Esempio n. 9
0
 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")
     )
Esempio n. 10
0
 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())
Esempio n. 11
0
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})
Esempio n. 12
0
    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/")
Esempio n. 13
0
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,
        })
Esempio n. 14
0
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,
            }
        )
Esempio n. 15
0
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)