def setUp(self): import tempfile self.basefolder = os.path.realpath(os.path.abspath(tempfile.mkdtemp())) self.storage = LocalFileStorage(self.basefolder) # mock file manager module self.filemanager_patcher = mock.patch("octoprint.filemanager") self.filemanager = self.filemanager_patcher.start() self.filemanager.valid_file_type.return_value = True def get_file_type(name): if name.lower().endswith(".stl"): return ["model", "stl"] elif ( name.lower().endswith(".gco") or name.lower().endswith(".gcode") or name.lower.endswith(".g") ): return ["machinecode", "gcode"] else: return None self.filemanager.get_file_type.side_effect = get_file_type
def setUp(self): import tempfile self.basefolder = os.path.realpath(os.path.abspath(tempfile.mkdtemp())) self.storage = LocalFileStorage(self.basefolder) # mock file manager module self.filemanager_patcher = mock.patch("octoprint.filemanager") self.filemanager = self.filemanager_patcher.start() self.filemanager.valid_file_type.return_value = True def get_file_type(name): if name.lower().endswith(".stl"): return ["model", "stl"] elif name.lower().endswith(".gco") or name.lower().endswith(".gcode") or name.lower.endswith(".g"): return ["machinecode", "gcode"] else: return None self.filemanager.get_file_type.side_effect = get_file_type
class LocalStorageTest(unittest.TestCase): def setUp(self): import tempfile self.basefolder = os.path.realpath(os.path.abspath(tempfile.mkdtemp())) self.storage = LocalFileStorage(self.basefolder) # mock file manager module self.filemanager_patcher = mock.patch("octoprint.filemanager") self.filemanager = self.filemanager_patcher.start() self.filemanager.valid_file_type.return_value = True def get_file_type(name): if name.lower().endswith(".stl"): return ["model", "stl"] elif name.lower().endswith(".gco") or name.lower().endswith(".gcode") or name.lower.endswith(".g"): return ["machinecode", "gcode"] else: return None self.filemanager.get_file_type.side_effect = get_file_type def tearDown(self): import shutil shutil.rmtree(self.basefolder) self.filemanager_patcher.stop() def test_add_file(self): self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) def test_add_file_overwrite(self): self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) try: self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL, overwrite=False) except: pass self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL, overwrite=True) def test_add_file_with_web(self): import time href = "http://www.example.com" retrieved = time.time() stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL, links=[("web", dict(href=href, retrieved=retrieved))]) stl_metadata = self.storage.get_metadata(stl_name) self.assertIsNotNone(stl_metadata) self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertTrue("web", link["rel"]) self.assertTrue("href" in link) self.assertEqual(href, link["href"]) self.assertTrue("retrieved" in link) self.assertEqual(retrieved, link["retrieved"]) def test_add_file_with_association(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE, links=[("model", dict(name=stl_name))]) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) # forward link self.assertEqual(1, len(gcode_metadata["links"])) link = gcode_metadata["links"][0] self.assertEqual("model", link["rel"]) self.assertTrue("name" in link) self.assertEqual(stl_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_STL.hash, link["hash"]) # reverse link self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertEqual("machinecode", link["rel"]) self.assertTrue("name" in link) self.assertEqual(gcode_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_GCODE.hash, link["hash"]) def test_remove_file(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE, links=[("model", dict(name=stl_name))]) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertIsNotNone(stl_metadata) self.assertIsNotNone(gcode_metadata) self.storage.remove_file(stl_name) self.assertFalse(os.path.exists(os.path.join(self.basefolder, stl_name))) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertIsNone(stl_metadata) self.assertIsNotNone(gcode_metadata) self.assertEqual(0, len(gcode_metadata["links"])) def test_copy_file(self): self._add_file("bp_case.stl", FILE_BP_CASE_STL) self._add_folder("test") self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "test"))) self.storage.copy_file("bp_case.stl", "test/copied.stl") self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "test", "copied.stl"))) stl_metadata = self.storage.get_metadata("bp_case.stl") copied_metadata = self.storage.get_metadata("test/copied.stl") self.assertIsNotNone(stl_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(stl_metadata, copied_metadata) def test_move_file(self): self._add_file("bp_case.stl", FILE_BP_CASE_STL) self._add_folder("test") self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "test"))) before_stl_metadata = self.storage.get_metadata("bp_case.stl") self.storage.move_file("bp_case.stl", "test/copied.stl") self.assertFalse(os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "test", "copied.stl"))) after_stl_metadata = self.storage.get_metadata("bp_case.stl") copied_metadata = self.storage.get_metadata("test/copied.stl") self.assertIsNotNone(before_stl_metadata) self.assertIsNone(after_stl_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(before_stl_metadata, copied_metadata) @data("copy_file", "move_file") def test_copy_move_file_missing_source(self, operation): try: getattr(self.storage, operation)("bp_case.stl", "test/copied.stl") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_SOURCE) @data("copy_file", "move_file") def test_copy_move_file_missing_destination_folder(self, operation): self._add_file("bp_case.stl", FILE_BP_CASE_STL) try: getattr(self.storage, operation)("bp_case.stl", "test/copied.stl") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) @data("copy_file", "move_file") def test_copy_move_file_existing_destination_path(self, operation): self._add_file("bp_case.stl", FILE_BP_CASE_STL) self._add_folder("test") self._add_file("test/crazyradio.stl", FILE_CRAZYRADIO_STL) try: getattr(self.storage, operation)("bp_case.stl", "test/crazyradio.stl") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) def test_add_folder(self): self._add_and_verify_folder("test", "test") def test_add_subfolder(self): folder_name = self._add_and_verify_folder("folder with some spaces", "folder_with_some_spaces") subfolder_name = self._add_and_verify_folder((folder_name, "subfolder"), folder_name + "/subfolder") stl_name = self._add_and_verify_file((subfolder_name, "bp_case.stl"), subfolder_name + "/bp_case.stl", FILE_BP_CASE_STL) self.assertTrue(os.path.exists(os.path.join(self.basefolder, folder_name))) self.assertTrue(os.path.exists(os.path.join(self.basefolder, subfolder_name))) self.assertTrue(os.path.exists(os.path.join(self.basefolder, stl_name))) def test_remove_folder(self): content_folder = self._add_and_verify_folder("content", "content") other_stl_name = self._add_and_verify_file((content_folder, "crazyradio.stl"), content_folder + "/crazyradio.stl", FILE_CRAZYRADIO_STL) empty_folder = self._add_and_verify_folder("empty", "empty") try: self.storage.remove_folder(content_folder, recursive=False) except: self.assertTrue(os.path.exists(os.path.join(self.basefolder, content_folder))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, content_folder))) self.assertTrue(os.path.exists(os.path.join(self.basefolder, other_stl_name))) self.assertIsNotNone(self.storage.get_metadata(other_stl_name)) self.storage.remove_folder(content_folder, recursive=True) self.assertFalse(os.path.exists(os.path.join(self.basefolder, content_folder))) self.assertFalse(os.path.isdir(os.path.join(self.basefolder, content_folder))) self.storage.remove_folder(empty_folder, recursive=False) self.assertFalse(os.path.exists(os.path.join(self.basefolder, empty_folder))) self.assertFalse(os.path.isdir(os.path.join(self.basefolder, empty_folder))) def test_remove_folder_with_metadata(self): content_folder = self._add_and_verify_folder("content", "content") other_stl_name = self._add_and_verify_file((content_folder, "crazyradio.stl"), content_folder + "/crazyradio.stl", FILE_CRAZYRADIO_STL) self.storage.remove_file(other_stl_name) self.storage.remove_folder(content_folder, recursive=False) def test_copy_folder(self): self._add_folder("source") self._add_folder("destination") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) source_metadata = self.storage.get_metadata("source/crazyradio.stl") self.storage.copy_folder("source", "destination/copied") copied_metadata = self.storage.get_metadata("destination/copied/crazyradio.stl") self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "source"))) self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "source", "crazyradio.stl"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "destination"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "destination", "copied"))) self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "destination", "copied", ".metadata.yaml"))) self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "destination", "copied", "crazyradio.stl"))) self.assertIsNotNone(source_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(source_metadata, copied_metadata) def test_move_folder(self): self._add_folder("source") self._add_folder("destination") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) before_source_metadata = self.storage.get_metadata("source/crazyradio.stl") self.storage.move_folder("source", "destination/copied") after_source_metadata = self.storage.get_metadata("source/crazyradio.stl") copied_metadata = self.storage.get_metadata("destination/copied/crazyradio.stl") self.assertFalse(os.path.isdir(os.path.join(self.basefolder, "source"))) self.assertFalse(os.path.isfile(os.path.join(self.basefolder, "source", "crazyradio.stl"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "destination"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "destination", "copied"))) self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "destination", "copied", ".metadata.yaml"))) self.assertTrue(os.path.isfile(os.path.join(self.basefolder, "destination", "copied", "crazyradio.stl"))) self.assertIsNotNone(before_source_metadata) self.assertIsNone(after_source_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(before_source_metadata, copied_metadata) @data("copy_folder", "move_folder") def test_copy_move_folder_missing_source(self, operation): try: getattr(self.storage, operation)("source", "destination/copied") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_SOURCE) @data("copy_folder", "move_folder") def test_copy_move_folder_missing_destination_folder(self, operation): self._add_folder("source") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) try: getattr(self.storage, operation)("source", "destination/copied") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) @data("copy_folder", "move_folder") def test_copy_move_folder_existing_destination_path(self, operation): self._add_folder("source") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) self._add_folder("destination") self._add_folder("destination/copied") try: getattr(self.storage, operation)("source", "destination/copied") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) def test_list(self): bp_case_stl = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE, links=[("model", dict(name=bp_case_stl))]) content_folder = self._add_and_verify_folder("content", "content") self._add_and_verify_file((content_folder, "crazyradio.stl"), content_folder + "/crazyradio.stl", FILE_CRAZYRADIO_STL) self._add_and_verify_folder("empty", "empty") file_list = self.storage.list_files() self.assertEqual(4, len(file_list)) self.assertTrue("bp_case.stl" in file_list) self.assertTrue("bp_case.gcode" in file_list) self.assertTrue("content" in file_list) self.assertTrue("empty" in file_list) self.assertEqual("model", file_list["bp_case.stl"]["type"]) self.assertEqual(FILE_BP_CASE_STL.hash, file_list["bp_case.stl"]["hash"]) self.assertEqual("machinecode", file_list["bp_case.gcode"]["type"]) self.assertEqual(FILE_BP_CASE_GCODE.hash, file_list["bp_case.gcode"]["hash"]) self.assertEqual("folder", file_list[content_folder]["type"]) self.assertEqual(1, len(file_list[content_folder]["children"])) self.assertTrue("crazyradio.stl" in file_list["content"]["children"]) self.assertEqual("model", file_list["content"]["children"]["crazyradio.stl"]["type"]) self.assertEqual(FILE_CRAZYRADIO_STL.hash, file_list["content"]["children"]["crazyradio.stl"]["hash"]) self.assertEqual("folder", file_list["empty"]["type"]) self.assertEqual(0, len(file_list["empty"]["children"])) def test_add_link_model(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE) self.storage.add_link(gcode_name, "model", dict(name=stl_name)) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) # forward link self.assertEqual(1, len(gcode_metadata["links"])) link = gcode_metadata["links"][0] self.assertEqual("model", link["rel"]) self.assertTrue("name" in link) self.assertEqual(stl_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_STL.hash, link["hash"]) # reverse link self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertEqual("machinecode", link["rel"]) self.assertTrue("name" in link) self.assertEqual(gcode_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_GCODE.hash, link["hash"]) def test_add_link_machinecode(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE) self.storage.add_link(stl_name, "machinecode", dict(name=gcode_name)) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) # forward link self.assertEqual(1, len(gcode_metadata["links"])) link = gcode_metadata["links"][0] self.assertEqual("model", link["rel"]) self.assertTrue("name" in link) self.assertEqual(stl_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_STL.hash, link["hash"]) # reverse link self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertEqual("machinecode", link["rel"]) self.assertTrue("name" in link) self.assertEqual(gcode_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_GCODE.hash, link["hash"]) def test_remove_link(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) self.storage.add_link(stl_name, "web", dict(href="http://www.example.com")) self.storage.add_link(stl_name, "web", dict(href="http://www.example2.com")) stl_metadata = self.storage.get_metadata(stl_name) self.assertEqual(2, len(stl_metadata["links"])) self.storage.remove_link(stl_name, "web", dict(href="http://www.example.com")) stl_metadata = self.storage.get_metadata(stl_name) self.assertEqual(1, len(stl_metadata["links"])) self.storage.remove_link(stl_name, "web", dict(href="wrong_href")) stl_metadata = self.storage.get_metadata(stl_name) self.assertEqual(1, len(stl_metadata["links"])) def test_remove_link_bidirectional(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE) self.storage.add_link(stl_name, "machinecode", dict(name=gcode_name)) self.storage.add_link(stl_name, "web", dict(href="http://www.example.com")) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertEqual(1, len(gcode_metadata["links"])) self.assertEqual(2, len(stl_metadata["links"])) self.storage.remove_link(gcode_name, "model", dict(name=stl_name, hash=FILE_BP_CASE_STL.hash)) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertEqual(0, len(gcode_metadata["links"])) self.assertEqual(1, len(stl_metadata["links"])) @data( ("some_file.gco", "some_file.gco"), ("some_file with (parentheses) and ümläuts and digits 123.gco", "some_file_with_(parentheses)_and_umlauts_and_digits_123.gco"), ("pengüino pequeño.stl", "penguino_pequeno.stl") ) @unpack def test_sanitize_name(self, input, expected): actual = self.storage.sanitize_name(input) self.assertEqual(expected, actual) @data( "some/folder/still/left.gco", "also\\no\\backslashes.gco" ) def test_sanitize_name_invalid(self, input): try: self.storage.sanitize_name(input) self.fail("expected a ValueError") except ValueError as e: self.assertEqual("name must not contain / or \\", e.message) @data( ("folder/with/subfolder", "/folder/with/subfolder"), ("folder/with/subfolder/../other/folder", "/folder/with/other/folder"), ("/folder/with/leading/slash", "/folder/with/leading/slash"), ("folder/with/leading/dot", "/folder/with/leading/dot") ) @unpack def test_sanitize_path(self, input, expected): actual = self.storage.sanitize_path(input) self.assertTrue(actual.startswith(self.basefolder)) self.assertEqual(expected, actual[len(self.basefolder):].replace(os.path.sep, "/")) @data( "../../folder/out/of/the/basefolder", "some/folder/../../../and/then/back" ) def test_sanitize_path_invalid(self, input): try: self.storage.sanitize_path(input) self.fail("expected a ValueError") except ValueError as e: self.assertTrue(e.message.startswith("path not contained in base folder: ")) @data( ("some/folder/and/some file.gco", "/some/folder/and", "some_file.gco"), (("some", "folder", "and", "some file.gco"), "/some/folder/and", "some_file.gco"), ("some file.gco", "/", "some_file.gco"), (("some file.gco",), "/", "some_file.gco"), ("", "/", ""), ("some/folder/with/trailing/slash/", "/some/folder/with/trailing/slash", ""), (("some", "folder", ""), "/some/folder", "") ) @unpack def test_sanitize(self, input, expected_path, expected_name): actual = self.storage.sanitize(input) self.assertTrue(isinstance(actual, tuple)) self.assertEqual(2, len(actual)) actual_path, actual_name = actual self.assertTrue(actual_path.startswith(self.basefolder)) actual_path = actual_path[len(self.basefolder):].replace(os.path.sep, "/") if not actual_path.startswith("/"): # if the actual path originally was just the base folder, we just stripped # away everything, so let's add a / again so the behaviour matches the # other preprocessing of our test data here actual_path = "/" + actual_path self.assertEqual(expected_path, actual_path) self.assertEqual(expected_name, actual_name) def _add_and_verify_file(self, path, expected_path, file_object, links=None, overwrite=False): """Adds a file to the storage and verifies the sanitized path.""" sanitized_path = self._add_file(path, file_object, links=links, overwrite=overwrite) self.assertEqual(expected_path, sanitized_path) return sanitized_path def _add_file(self, path, file_object, links=None, overwrite=False): """ Adds a file to the storage. Ensures file is present, metadata is present, hash and links (if applicable) are populated correctly. Returns sanitized path. """ sanitized_path = self.storage.add_file(path, file_object, links=links, allow_overwrite=overwrite) split_path = sanitized_path.split("/") if len(split_path) == 1: file_path = os.path.join(self.basefolder, split_path[0]) folder_path = self.basefolder else: file_path = os.path.join(self.basefolder, os.path.join(*split_path)) folder_path = os.path.join(self.basefolder, os.path.join(*split_path[:-1])) self.assertTrue(os.path.isfile(file_path)) self.assertTrue(os.path.isfile(os.path.join(folder_path, ".metadata.yaml"))) metadata = self.storage.get_metadata(sanitized_path) self.assertIsNotNone(metadata) # assert hash self.assertTrue("hash" in metadata) self.assertEqual(file_object.hash, metadata["hash"]) # assert presence of links if supplied if links: self.assertTrue("links" in metadata) return sanitized_path def _add_and_verify_folder(self, path, expected_path): """Adds a folder to the storage and verifies sanitized path.""" sanitized_path = self._add_folder(path) self.assertEqual(expected_path, sanitized_path) return sanitized_path def _add_folder(self, path): """ Adds a folder to the storage. Verifies existance of folder. Returns sanitized path. """ sanitized_path = self.storage.add_folder(path) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, os.path.join(*sanitized_path.split("/"))))) return sanitized_path
class LocalStorageTest(unittest.TestCase): def setUp(self): import tempfile self.basefolder = os.path.realpath(os.path.abspath(tempfile.mkdtemp())) self.storage = LocalFileStorage(self.basefolder) # mock file manager module self.filemanager_patcher = mock.patch("octoprint.filemanager") self.filemanager = self.filemanager_patcher.start() self.filemanager.valid_file_type.return_value = True def get_file_type(name): if name.lower().endswith(".stl"): return ["model", "stl"] elif name.lower().endswith(".gco") or name.lower().endswith( ".gcode") or name.lower.endswith(".g"): return ["machinecode", "gcode"] else: return None self.filemanager.get_file_type.side_effect = get_file_type def tearDown(self): import shutil shutil.rmtree(self.basefolder) self.filemanager_patcher.stop() def test_add_file(self): self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) def test_add_file_overwrite(self): self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) try: self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL, overwrite=False) except: pass self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL, overwrite=True) def test_add_file_with_web(self): import time href = "http://www.example.com" retrieved = time.time() stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL, links=[("web", dict(href=href, retrieved=retrieved)) ]) stl_metadata = self.storage.get_metadata(stl_name) self.assertIsNotNone(stl_metadata) self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertTrue("web", link["rel"]) self.assertTrue("href" in link) self.assertEqual(href, link["href"]) self.assertTrue("retrieved" in link) self.assertEqual(retrieved, link["retrieved"]) def test_add_file_with_association(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE, links=[("model", dict(name=stl_name))]) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) # forward link self.assertEqual(1, len(gcode_metadata["links"])) link = gcode_metadata["links"][0] self.assertEqual("model", link["rel"]) self.assertTrue("name" in link) self.assertEqual(stl_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_STL.hash, link["hash"]) # reverse link self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertEqual("machinecode", link["rel"]) self.assertTrue("name" in link) self.assertEqual(gcode_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_GCODE.hash, link["hash"]) def test_remove_file(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE, links=[("model", dict(name=stl_name))]) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertIsNotNone(stl_metadata) self.assertIsNotNone(gcode_metadata) self.storage.remove_file(stl_name) self.assertFalse( os.path.exists(os.path.join(self.basefolder, stl_name))) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertIsNone(stl_metadata) self.assertIsNotNone(gcode_metadata) self.assertEqual(0, len(gcode_metadata["links"])) def test_copy_file(self): self._add_file("bp_case.stl", FILE_BP_CASE_STL) self._add_folder("test") self.assertTrue( os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "test"))) self.storage.copy_file("bp_case.stl", "test/copied.stl") self.assertTrue( os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue( os.path.isfile(os.path.join(self.basefolder, "test", "copied.stl"))) stl_metadata = self.storage.get_metadata("bp_case.stl") copied_metadata = self.storage.get_metadata("test/copied.stl") self.assertIsNotNone(stl_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(stl_metadata, copied_metadata) def test_move_file(self): self._add_file("bp_case.stl", FILE_BP_CASE_STL) self._add_folder("test") self.assertTrue( os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "test"))) before_stl_metadata = self.storage.get_metadata("bp_case.stl") self.storage.move_file("bp_case.stl", "test/copied.stl") self.assertFalse( os.path.isfile(os.path.join(self.basefolder, "bp_case.stl"))) self.assertTrue( os.path.isfile(os.path.join(self.basefolder, "test", "copied.stl"))) after_stl_metadata = self.storage.get_metadata("bp_case.stl") copied_metadata = self.storage.get_metadata("test/copied.stl") self.assertIsNotNone(before_stl_metadata) self.assertIsNone(after_stl_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(before_stl_metadata, copied_metadata) @data("copy_file", "move_file") def test_copy_move_file_missing_source(self, operation): try: getattr(self.storage, operation)("bp_case.stl", "test/copied.stl") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_SOURCE) @data("copy_file", "move_file") def test_copy_move_file_missing_destination_folder(self, operation): self._add_file("bp_case.stl", FILE_BP_CASE_STL) try: getattr(self.storage, operation)("bp_case.stl", "test/copied.stl") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) @data("copy_file", "move_file") def test_copy_move_file_existing_destination_path(self, operation): self._add_file("bp_case.stl", FILE_BP_CASE_STL) self._add_folder("test") self._add_file("test/crazyradio.stl", FILE_CRAZYRADIO_STL) try: getattr(self.storage, operation)("bp_case.stl", "test/crazyradio.stl") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) def test_add_folder(self): self._add_and_verify_folder("test", "test") def test_add_subfolder(self): folder_name = self._add_and_verify_folder("folder with some spaces", "folder_with_some_spaces") subfolder_name = self._add_and_verify_folder( (folder_name, "subfolder"), folder_name + "/subfolder") stl_name = self._add_and_verify_file((subfolder_name, "bp_case.stl"), subfolder_name + "/bp_case.stl", FILE_BP_CASE_STL) self.assertTrue( os.path.exists(os.path.join(self.basefolder, folder_name))) self.assertTrue( os.path.exists(os.path.join(self.basefolder, subfolder_name))) self.assertTrue(os.path.exists(os.path.join(self.basefolder, stl_name))) def test_remove_folder(self): content_folder = self._add_and_verify_folder("content", "content") other_stl_name = self._add_and_verify_file( (content_folder, "crazyradio.stl"), content_folder + "/crazyradio.stl", FILE_CRAZYRADIO_STL) empty_folder = self._add_and_verify_folder("empty", "empty") try: self.storage.remove_folder(content_folder, recursive=False) except: self.assertTrue( os.path.exists(os.path.join(self.basefolder, content_folder))) self.assertTrue( os.path.isdir(os.path.join(self.basefolder, content_folder))) self.assertTrue( os.path.exists(os.path.join(self.basefolder, other_stl_name))) self.assertIsNotNone(self.storage.get_metadata(other_stl_name)) self.storage.remove_folder(content_folder, recursive=True) self.assertFalse( os.path.exists(os.path.join(self.basefolder, content_folder))) self.assertFalse( os.path.isdir(os.path.join(self.basefolder, content_folder))) self.storage.remove_folder(empty_folder, recursive=False) self.assertFalse( os.path.exists(os.path.join(self.basefolder, empty_folder))) self.assertFalse( os.path.isdir(os.path.join(self.basefolder, empty_folder))) def test_remove_folder_with_metadata(self): content_folder = self._add_and_verify_folder("content", "content") other_stl_name = self._add_and_verify_file( (content_folder, "crazyradio.stl"), content_folder + "/crazyradio.stl", FILE_CRAZYRADIO_STL) self.storage.remove_file(other_stl_name) self.storage.remove_folder(content_folder, recursive=False) def test_copy_folder(self): self._add_folder("source") self._add_folder("destination") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) source_metadata = self.storage.get_metadata("source/crazyradio.stl") self.storage.copy_folder("source", "destination/copied") copied_metadata = self.storage.get_metadata( "destination/copied/crazyradio.stl") self.assertTrue(os.path.isdir(os.path.join(self.basefolder, "source"))) self.assertTrue( os.path.isfile( os.path.join(self.basefolder, "source", "crazyradio.stl"))) self.assertTrue( os.path.isdir(os.path.join(self.basefolder, "destination"))) self.assertTrue( os.path.isdir( os.path.join(self.basefolder, "destination", "copied"))) self.assertTrue( os.path.isfile( os.path.join(self.basefolder, "destination", "copied", ".metadata.yaml"))) self.assertTrue( os.path.isfile( os.path.join(self.basefolder, "destination", "copied", "crazyradio.stl"))) self.assertIsNotNone(source_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(source_metadata, copied_metadata) def test_move_folder(self): self._add_folder("source") self._add_folder("destination") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) before_source_metadata = self.storage.get_metadata( "source/crazyradio.stl") self.storage.move_folder("source", "destination/copied") after_source_metadata = self.storage.get_metadata( "source/crazyradio.stl") copied_metadata = self.storage.get_metadata( "destination/copied/crazyradio.stl") self.assertFalse(os.path.isdir(os.path.join(self.basefolder, "source"))) self.assertFalse( os.path.isfile( os.path.join(self.basefolder, "source", "crazyradio.stl"))) self.assertTrue( os.path.isdir(os.path.join(self.basefolder, "destination"))) self.assertTrue( os.path.isdir( os.path.join(self.basefolder, "destination", "copied"))) self.assertTrue( os.path.isfile( os.path.join(self.basefolder, "destination", "copied", ".metadata.yaml"))) self.assertTrue( os.path.isfile( os.path.join(self.basefolder, "destination", "copied", "crazyradio.stl"))) self.assertIsNotNone(before_source_metadata) self.assertIsNone(after_source_metadata) self.assertIsNotNone(copied_metadata) self.assertDictEqual(before_source_metadata, copied_metadata) @data("copy_folder", "move_folder") def test_copy_move_folder_missing_source(self, operation): try: getattr(self.storage, operation)("source", "destination/copied") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_SOURCE) @data("copy_folder", "move_folder") def test_copy_move_folder_missing_destination_folder(self, operation): self._add_folder("source") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) try: getattr(self.storage, operation)("source", "destination/copied") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) @data("copy_folder", "move_folder") def test_copy_move_folder_existing_destination_path(self, operation): self._add_folder("source") self._add_file("source/crazyradio.stl", FILE_CRAZYRADIO_STL) self._add_folder("destination") self._add_folder("destination/copied") try: getattr(self.storage, operation)("source", "destination/copied") self.fail("Expected an exception") except StorageError as e: self.assertEqual(e.code, StorageError.INVALID_DESTINATION) def test_list(self): bp_case_stl = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE, links=[("model", dict(name=bp_case_stl))]) content_folder = self._add_and_verify_folder("content", "content") self._add_and_verify_file((content_folder, "crazyradio.stl"), content_folder + "/crazyradio.stl", FILE_CRAZYRADIO_STL) self._add_and_verify_folder("empty", "empty") file_list = self.storage.list_files() self.assertEqual(4, len(file_list)) self.assertTrue("bp_case.stl" in file_list) self.assertTrue("bp_case.gcode" in file_list) self.assertTrue("content" in file_list) self.assertTrue("empty" in file_list) self.assertEqual("model", file_list["bp_case.stl"]["type"]) self.assertEqual(FILE_BP_CASE_STL.hash, file_list["bp_case.stl"]["hash"]) self.assertEqual("machinecode", file_list["bp_case.gcode"]["type"]) self.assertEqual(FILE_BP_CASE_GCODE.hash, file_list["bp_case.gcode"]["hash"]) self.assertEqual("folder", file_list[content_folder]["type"]) self.assertEqual(1, len(file_list[content_folder]["children"])) self.assertTrue("crazyradio.stl" in file_list["content"]["children"]) self.assertEqual( "model", file_list["content"]["children"]["crazyradio.stl"]["type"]) self.assertEqual( FILE_CRAZYRADIO_STL.hash, file_list["content"]["children"]["crazyradio.stl"]["hash"]) self.assertEqual("folder", file_list["empty"]["type"]) self.assertEqual(0, len(file_list["empty"]["children"])) def test_add_link_model(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE) self.storage.add_link(gcode_name, "model", dict(name=stl_name)) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) # forward link self.assertEqual(1, len(gcode_metadata["links"])) link = gcode_metadata["links"][0] self.assertEqual("model", link["rel"]) self.assertTrue("name" in link) self.assertEqual(stl_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_STL.hash, link["hash"]) # reverse link self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertEqual("machinecode", link["rel"]) self.assertTrue("name" in link) self.assertEqual(gcode_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_GCODE.hash, link["hash"]) def test_add_link_machinecode(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE) self.storage.add_link(stl_name, "machinecode", dict(name=gcode_name)) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) # forward link self.assertEqual(1, len(gcode_metadata["links"])) link = gcode_metadata["links"][0] self.assertEqual("model", link["rel"]) self.assertTrue("name" in link) self.assertEqual(stl_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_STL.hash, link["hash"]) # reverse link self.assertEqual(1, len(stl_metadata["links"])) link = stl_metadata["links"][0] self.assertEqual("machinecode", link["rel"]) self.assertTrue("name" in link) self.assertEqual(gcode_name, link["name"]) self.assertTrue("hash" in link) self.assertEqual(FILE_BP_CASE_GCODE.hash, link["hash"]) def test_remove_link(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) self.storage.add_link(stl_name, "web", dict(href="http://www.example.com")) self.storage.add_link(stl_name, "web", dict(href="http://www.example2.com")) stl_metadata = self.storage.get_metadata(stl_name) self.assertEqual(2, len(stl_metadata["links"])) self.storage.remove_link(stl_name, "web", dict(href="http://www.example.com")) stl_metadata = self.storage.get_metadata(stl_name) self.assertEqual(1, len(stl_metadata["links"])) self.storage.remove_link(stl_name, "web", dict(href="wrong_href")) stl_metadata = self.storage.get_metadata(stl_name) self.assertEqual(1, len(stl_metadata["links"])) def test_remove_link_bidirectional(self): stl_name = self._add_and_verify_file("bp_case.stl", "bp_case.stl", FILE_BP_CASE_STL) gcode_name = self._add_and_verify_file("bp_case.gcode", "bp_case.gcode", FILE_BP_CASE_GCODE) self.storage.add_link(stl_name, "machinecode", dict(name=gcode_name)) self.storage.add_link(stl_name, "web", dict(href="http://www.example.com")) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertEqual(1, len(gcode_metadata["links"])) self.assertEqual(2, len(stl_metadata["links"])) self.storage.remove_link( gcode_name, "model", dict(name=stl_name, hash=FILE_BP_CASE_STL.hash)) stl_metadata = self.storage.get_metadata(stl_name) gcode_metadata = self.storage.get_metadata(gcode_name) self.assertEqual(0, len(gcode_metadata["links"])) self.assertEqual(1, len(stl_metadata["links"])) @data(("some_file.gco", "some_file.gco"), ("some_file with (parentheses) and ümläuts and digits 123.gco", "some_file_with_(parentheses)_and_umlauts_and_digits_123.gco"), ("pengüino pequeño.stl", "penguino_pequeno.stl")) @unpack def test_sanitize_name(self, input, expected): actual = self.storage.sanitize_name(input) self.assertEqual(expected, actual) @data("some/folder/still/left.gco", "also\\no\\backslashes.gco") def test_sanitize_name_invalid(self, input): try: self.storage.sanitize_name(input) self.fail("expected a ValueError") except ValueError as e: self.assertEqual("name must not contain / or \\", e.message) @data( ("folder/with/subfolder", "/folder/with/subfolder"), ("folder/with/subfolder/../other/folder", "/folder/with/other/folder"), ("/folder/with/leading/slash", "/folder/with/leading/slash"), ("folder/with/leading/dot", "/folder/with/leading/dot")) @unpack def test_sanitize_path(self, input, expected): actual = self.storage.sanitize_path(input) self.assertTrue(actual.startswith(self.basefolder)) self.assertEqual( expected, actual[len(self.basefolder):].replace(os.path.sep, "/")) @data("../../folder/out/of/the/basefolder", "some/folder/../../../and/then/back") def test_sanitize_path_invalid(self, input): try: self.storage.sanitize_path(input) self.fail("expected a ValueError") except ValueError as e: self.assertTrue( e.message.startswith("path not contained in base folder: ")) @data( ("some/folder/and/some file.gco", "/some/folder/and", "some_file.gco"), (("some", "folder", "and", "some file.gco"), "/some/folder/and", "some_file.gco"), ("some file.gco", "/", "some_file.gco"), (("some file.gco", ), "/", "some_file.gco"), ("", "/", ""), ("some/folder/with/trailing/slash/", "/some/folder/with/trailing/slash", ""), (("some", "folder", ""), "/some/folder", "")) @unpack def test_sanitize(self, input, expected_path, expected_name): actual = self.storage.sanitize(input) self.assertTrue(isinstance(actual, tuple)) self.assertEqual(2, len(actual)) actual_path, actual_name = actual self.assertTrue(actual_path.startswith(self.basefolder)) actual_path = actual_path[len(self.basefolder):].replace( os.path.sep, "/") if not actual_path.startswith("/"): # if the actual path originally was just the base folder, we just stripped # away everything, so let's add a / again so the behaviour matches the # other preprocessing of our test data here actual_path = "/" + actual_path self.assertEqual(expected_path, actual_path) self.assertEqual(expected_name, actual_name) def _add_and_verify_file(self, path, expected_path, file_object, links=None, overwrite=False): """Adds a file to the storage and verifies the sanitized path.""" sanitized_path = self._add_file(path, file_object, links=links, overwrite=overwrite) self.assertEqual(expected_path, sanitized_path) return sanitized_path def _add_file(self, path, file_object, links=None, overwrite=False): """ Adds a file to the storage. Ensures file is present, metadata is present, hash and links (if applicable) are populated correctly. Returns sanitized path. """ sanitized_path = self.storage.add_file(path, file_object, links=links, allow_overwrite=overwrite) split_path = sanitized_path.split("/") if len(split_path) == 1: file_path = os.path.join(self.basefolder, split_path[0]) folder_path = self.basefolder else: file_path = os.path.join(self.basefolder, os.path.join(*split_path)) folder_path = os.path.join(self.basefolder, os.path.join(*split_path[:-1])) self.assertTrue(os.path.isfile(file_path)) self.assertTrue( os.path.isfile(os.path.join(folder_path, ".metadata.yaml"))) metadata = self.storage.get_metadata(sanitized_path) self.assertIsNotNone(metadata) # assert hash self.assertTrue("hash" in metadata) self.assertEqual(file_object.hash, metadata["hash"]) # assert presence of links if supplied if links: self.assertTrue("links" in metadata) return sanitized_path def _add_and_verify_folder(self, path, expected_path): """Adds a folder to the storage and verifies sanitized path.""" sanitized_path = self._add_folder(path) self.assertEqual(expected_path, sanitized_path) return sanitized_path def _add_folder(self, path): """ Adds a folder to the storage. Verifies existance of folder. Returns sanitized path. """ sanitized_path = self.storage.add_folder(path) self.assertTrue( os.path.isdir( os.path.join(self.basefolder, os.path.join(*sanitized_path.split("/"))))) return sanitized_path
def test_slugify(self, input, expected): output = LocalFileStorage._slugify(input) self.assertEqual(output, expected)
def __init__(self, show_files_callback, refresh_files, show_folders_callback, create_folder_callback, **kwargs): self.buttons = [] super(FileOptions, self).__init__(self.buttons) self.waiting_for_input = False self.show_folders_callback = show_folders_callback self.settings = roboprinter.printer_instance._settings self.show_files_callback = show_files_callback self.refresh_files = refresh_files self.create_folder_callback = create_folder_callback self.original_screen = None self.sort_observer = Button_Group_Observer() self.modify_files_observer = Button_Group_Observer() #initialize this so that we can edit files on octoprint. base_folder = '/home/pi/.octoprint/uploads' self.storage = LocalFileStorage(base_folder) self.show_selectable_files_options = { 'callback': self.get_selected_files, 'icon': "Icons/Files_Icons/File_Options/Next.png" } self.button_dict = { 'root_list': { 'buttons': [ File_Option_Button( name=lang.pack['Files']['File_Options']['Sort_Files'], callback=partial(self.switch_lists, next_list='sort_files'), default_icon= "Icons/Files_Icons/File_Options/Sort List.png"), File_Option_Button( name=lang.pack['Files']['File_Options'] ['Select_Items'], callback=partial( self.show_files_callback, callback_dict=self.show_selectable_files_options), default_icon= "Icons/Files_Icons/File_Options/Select items.png"), File_Option_Button( name=lang.pack['Files']['File_Options'] ['Create_Folder'], callback=self.create_folder_callback, default_icon= "Icons/Files_Icons/File_Options/Create new folder.png" ), File_Option_Button( name=lang.pack['Files']['File_Options']['Refresh'], callback=self.refresh, default_icon= "Icons/Files_Icons/File_Options/Refresh.png") ], 'title': lang.pack['Files']['File_Options']['Title'], 'option_function': self.exit_from_file_explorer, 'option_icon': "Icons/cancel_button_icon.png" }, 'sort_files': { 'buttons': [ File_Option_Button( name=lang.pack['Files']['Sort_Files']['Alphabet'], default_icon= "Icons/Files_Icons/File_Options/Alphabetically.png", observer=self.sort_observer, selected=False, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files'] ['Alphabet_Options']['A2Z'], lang.pack['Files'] ['Sort_Files']['Alphabet_Options']['Z2A'] ]), File_Option_Button( name=lang.pack['Files']['Sort_Files']['Size'], observer=self.sort_observer, default_icon= "Icons/Files_Icons/File_Options/By size.png", selected=False, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files']['Size_Options'] ['L2S'], lang.pack['Files']['Sort_Files'] ['Size_Options']['S2L'] ]), File_Option_Button( name=lang.pack['Files']['Sort_Files']['Date'], observer=self.sort_observer, default_icon= "Icons/Files_Icons/File_Options/By date.png", selected=True, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files']['Date_Options'] ['New'], lang.pack['Files']['Sort_Files'] ['Date_Options']['Old'] ]), File_Option_Button( name=lang.pack['Files']['Sort_Files']['Type'], observer=self.sort_observer, default_icon= "Icons/Files_Icons/File_Options/By type.png", selected=False, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files']['Type_Options'] ['STL'], lang.pack['Files']['Sort_Files'] ['Type_Options']['GCODE'], lang.pack['Files'] ['Sort_Files']['Type_Options']['HEX'], lang.pack['Files']['Sort_Files']['Type_Options'] ['Folder'] ]) ], 'title': lang.pack['Files']['Sort_Files']['Title'], 'option_function': self.set_sorting_options, 'option_icon': "Icons/Files_Icons/File_Options/Next.png" }, 'modify_files': { 'buttons': [ File_Option_Button( default_icon= "Icons/Files_Icons/File_Options/Copy files.png", selected_icon="Icons/check_icon.png", name=lang.pack['Files']['Modify_Files']['Copy'], observer=self.modify_files_observer, selected=False), File_Option_Button( default_icon= "Icons/Files_Icons/File_Options/Move files.png", selected_icon="Icons/check_icon.png", name=lang.pack['Files']['Modify_Files']['Move'], observer=self.modify_files_observer, selected=False), File_Option_Button( default_icon= "Icons/Files_Icons/File_Options/Delete files.png", selected_icon="Icons/check_icon.png", name=lang.pack['Files']['Modify_Files']['Delete'], observer=self.modify_files_observer, selected=False), File_Counter("0") ], 'title': lang.pack['Files']['Modify_Files']['Title'], 'option_function': self.modify_files, 'option_icon': "Icons/Files_Icons/File_Options/Next.png" } } self.cur_list = Screen_Node(screen=self.button_dict['root_list']) self.populate_screen() self.set_default_sorting_option()
class FileOptions(Scroll_Box_Even): ''' List Options will have a few different screens in it containing different lists of options. First List = Sort, Select Items to Edit, Create new Folder, Refresh These lists spawn different lists. instead of constantly switching out screens we are going to use one screen and update Scroll_Box_Even with a new list as needed. ''' def __init__(self, show_files_callback, refresh_files, show_folders_callback, create_folder_callback, **kwargs): self.buttons = [] super(FileOptions, self).__init__(self.buttons) self.waiting_for_input = False self.show_folders_callback = show_folders_callback self.settings = roboprinter.printer_instance._settings self.show_files_callback = show_files_callback self.refresh_files = refresh_files self.create_folder_callback = create_folder_callback self.original_screen = None self.sort_observer = Button_Group_Observer() self.modify_files_observer = Button_Group_Observer() #initialize this so that we can edit files on octoprint. base_folder = '/home/pi/.octoprint/uploads' self.storage = LocalFileStorage(base_folder) self.show_selectable_files_options = { 'callback': self.get_selected_files, 'icon': "Icons/Files_Icons/File_Options/Next.png" } self.button_dict = { 'root_list': { 'buttons': [ File_Option_Button( name=lang.pack['Files']['File_Options']['Sort_Files'], callback=partial(self.switch_lists, next_list='sort_files'), default_icon= "Icons/Files_Icons/File_Options/Sort List.png"), File_Option_Button( name=lang.pack['Files']['File_Options'] ['Select_Items'], callback=partial( self.show_files_callback, callback_dict=self.show_selectable_files_options), default_icon= "Icons/Files_Icons/File_Options/Select items.png"), File_Option_Button( name=lang.pack['Files']['File_Options'] ['Create_Folder'], callback=self.create_folder_callback, default_icon= "Icons/Files_Icons/File_Options/Create new folder.png" ), File_Option_Button( name=lang.pack['Files']['File_Options']['Refresh'], callback=self.refresh, default_icon= "Icons/Files_Icons/File_Options/Refresh.png") ], 'title': lang.pack['Files']['File_Options']['Title'], 'option_function': self.exit_from_file_explorer, 'option_icon': "Icons/cancel_button_icon.png" }, 'sort_files': { 'buttons': [ File_Option_Button( name=lang.pack['Files']['Sort_Files']['Alphabet'], default_icon= "Icons/Files_Icons/File_Options/Alphabetically.png", observer=self.sort_observer, selected=False, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files'] ['Alphabet_Options']['A2Z'], lang.pack['Files'] ['Sort_Files']['Alphabet_Options']['Z2A'] ]), File_Option_Button( name=lang.pack['Files']['Sort_Files']['Size'], observer=self.sort_observer, default_icon= "Icons/Files_Icons/File_Options/By size.png", selected=False, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files']['Size_Options'] ['L2S'], lang.pack['Files']['Sort_Files'] ['Size_Options']['S2L'] ]), File_Option_Button( name=lang.pack['Files']['Sort_Files']['Date'], observer=self.sort_observer, default_icon= "Icons/Files_Icons/File_Options/By date.png", selected=True, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files']['Date_Options'] ['New'], lang.pack['Files']['Sort_Files'] ['Date_Options']['Old'] ]), File_Option_Button( name=lang.pack['Files']['Sort_Files']['Type'], observer=self.sort_observer, default_icon= "Icons/Files_Icons/File_Options/By type.png", selected=False, extra_content=True, option_list=[ lang.pack['Files']['Sort_Files']['Type_Options'] ['STL'], lang.pack['Files']['Sort_Files'] ['Type_Options']['GCODE'], lang.pack['Files'] ['Sort_Files']['Type_Options']['HEX'], lang.pack['Files']['Sort_Files']['Type_Options'] ['Folder'] ]) ], 'title': lang.pack['Files']['Sort_Files']['Title'], 'option_function': self.set_sorting_options, 'option_icon': "Icons/Files_Icons/File_Options/Next.png" }, 'modify_files': { 'buttons': [ File_Option_Button( default_icon= "Icons/Files_Icons/File_Options/Copy files.png", selected_icon="Icons/check_icon.png", name=lang.pack['Files']['Modify_Files']['Copy'], observer=self.modify_files_observer, selected=False), File_Option_Button( default_icon= "Icons/Files_Icons/File_Options/Move files.png", selected_icon="Icons/check_icon.png", name=lang.pack['Files']['Modify_Files']['Move'], observer=self.modify_files_observer, selected=False), File_Option_Button( default_icon= "Icons/Files_Icons/File_Options/Delete files.png", selected_icon="Icons/check_icon.png", name=lang.pack['Files']['Modify_Files']['Delete'], observer=self.modify_files_observer, selected=False), File_Counter("0") ], 'title': lang.pack['Files']['Modify_Files']['Title'], 'option_function': self.modify_files, 'option_icon': "Icons/Files_Icons/File_Options/Next.png" } } self.cur_list = Screen_Node(screen=self.button_dict['root_list']) self.populate_screen() self.set_default_sorting_option() def exit_from_file_explorer(self): #back function for cancel and the backbutton back_destination = roboprinter.screen_controls.get_screen_data() back_function = partial( roboprinter.screen_controls.populate_old_screen, screen=back_destination) def confirm_exit(): body_text = lang.pack['Files']['File_Options'][ 'Exit_File_Options']['Body'] option1 = lang.pack['Files']['File_Options']['Exit_File_Options'][ 'Option1'] option2 = lang.pack['Files']['File_Options']['Exit_File_Options'][ 'Option2'] modal_screen = Modal_Question_No_Title(body_text, option1, option2, exit_fe, cancel_action) #add to the screen roboprinter.screen_controls.set_screen_content( modal_screen, lang.pack['Files']['File_Options']['Exit_File_Options'] ['Title'], back_function=back_function, option_function='no_option') def cancel_action(): back_function() def exit_fe(): roboprinter.robosm.go_back_to_main('printer_status_tab') confirm_exit() def switch_lists(self, next_list): if next_list in self.button_dict: self.cur_list = Screen_Node(screen=self.button_dict[next_list], prev_screen=self.cur_list) self.populate_screen() return True return False def return_to_previous_list(self): if self.cur_list.prev_screen != None: self.cur_list = self.cur_list.prev_screen self.populate_screen() return True else: if self.original_screen != None: roboprinter.screen_controls.populate_old_screen( self.original_screen) return False def refresh(self, *args, **kwargs): #refresh File Lists self.refresh_files() roboprinter.screen_controls.populate_old_screen(self.original_screen) def populate_screen(self): self.buttons = self.cur_list.screen['buttons'] self.title = self.cur_list.screen['title'] self.repopulate_for_new_screen() #remake the File_BB screen roboprinter.screen_controls.update_title(self.title) #change the option function as needed if 'option_function' in self.cur_list.screen: roboprinter.screen_controls.update_option_function( self.cur_list.screen['option_function'], self.cur_list.screen['option_icon']) #This function links to the sorting buttons to detirmine what the sorting order of files should be #it will store this as a dictionary with the sort type and the option that goes along with that sorting type. def set_sorting_options(self): #grab all of the button states for option in self.button_dict['sort_files']['buttons']: if option.selected: self.sorting_option = { 'sort': option.original_name, 'modify': option.option_list[option.list_pos] } break #save the sorting option to settings self.settings.set(['sorting_config'], self.sorting_option) self.settings.save() #Go back to the file screen. self.return_to_previous_list() if self.original_screen != None: roboprinter.screen_controls.populate_old_screen( self.original_screen) def set_default_sorting_option(self): #get the saved sorting option self.sorting_option = self.settings.get(['sorting_config']) #Protection for new machines that do not have this value initialized yet. robo_sorting_options = [ lang.pack['Files']['Sort_Files']['Type'], lang.pack['Files']['Sort_Files']['Size'], lang.pack['Files']['Sort_Files']['Date'], lang.pack['Files']['Sort_Files']['Alphabet'] ] if self.sorting_option == {} or not self.sorting_option[ 'sort'] in robo_sorting_options: self.set_sorting_options() #set that button to active for button in self.button_dict['sort_files']['buttons']: if button.original_name == self.sorting_option['sort']: button.select(True) for x in range(0, len(button.option_list)): if button.option_list[x] == self.sorting_option['modify']: button.list_pos = x break break def get_sorting_options(self): return self.sorting_option #this callback has the previous screen passed to it, so it can restore the File_options object then switch the #layout of that screen. def get_selected_files(self, file_list, file_options_screen, resume_file_select, update_selected_folders): #Logger.info("Getting selected files") self.selected_files = [] selected_folders_path = {} for file in file_list: if file['selected']: #Logger.info('File: ' + str(file['name']) + ' added to list') self.selected_files.append(file) if file['type'] == 'folder': selected_folders_path[file['path']] = file['name'] #if nothing is selected then do nothing if len(self.selected_files) == 0: #Logger.info("No Items Selected") ep = Error_Popup( lang.pack['Files']['Errors']['No_Items1']['Title'], lang.pack['Files']['Errors']['No_Items1']['Body'], ) ep.show() return #return modify buttons to their original state #This is so the user can pick their option without interference from us for button in self.button_dict['modify_files']['buttons']: if hasattr(button, 'selected'): button.selected = False button.select(button.selected) elif hasattr(button, 'update_count'): button.update_count(str(len(self.selected_files))) else: raise AttributeError("Has neither selected or update count.") #Throw the selected list over to file_explorer so it can block out any update_selected_folders(selected_folders_path) #change screen back to lead back to the file select screen file_select_screen = roboprinter.screen_controls.get_screen_data() roboprinter.screen_controls.populate_old_screen(file_options_screen) self.switch_lists('modify_files') def back_function(): self.return_to_previous_list() #return to the List Option Page resume_file_select( ) #tell the File Explorer to go back to the file select screen roboprinter.screen_controls.populate_old_screen( file_select_screen, ignore_update=True) #return to the file select screen roboprinter.screen_controls.update_back_function(back_function) def modify_files(self): #Logger.info("Modify the files!") for button in self.button_dict['modify_files']['buttons']: if hasattr(button, 'selected'): if button.selected: self.execute_modification(button.original_name) return #Logger.info("No Items Selected") ep = Error_Popup( lang.pack['Files']['Errors']['No_Items2']['Title'], lang.pack['Files']['Errors']['No_Items2']['Body'], ) ep.show() #The code will only reach here if we did not find a selected button #Logger.info("No Option Selected!") def execute_modification(self, modify_option): self.modify_progress = File_Progress() self.modify_progress.update_title("[size=40]" + modify_option) #make the popup self.prog_popup = Empty_Popup(self.modify_progress) if modify_option == lang.pack['Files']['Modify_Files']['Delete']: #back function for cancel and the backbutton back_destination = roboprinter.screen_controls.get_screen_data() back_function = partial( roboprinter.screen_controls.populate_old_screen, screen=back_destination) def confirm_delete(): body_text = lang.pack['Files']['Delete_File_Conf']['Body'] option1 = lang.pack['Files']['Delete_File_Conf']['positive'] option2 = lang.pack['Files']['Delete_File_Conf']['negative'] modal_screen = Modal_Question_No_Title(body_text, option1, option2, delete_action, cancel_action) #add to the screen roboprinter.screen_controls.set_screen_content( modal_screen, lang.pack['Files']['Delete_File_Conf']['Title'], back_function=back_function, option_function='no_option') def cancel_action(): back_function() def delete_action(): self.delete_files() while (self.return_to_previous_list()): pass confirm_delete() elif modify_option == lang.pack['Files']['Modify_Files']['Move']: #Logger.info("Moving files") self.MODE = 'MOVE' show_selectable_folders_options = { 'callback': self.get_directory, 'icon': "Icons/Files_Icons/File_Options/Next.png" } self.show_folders_callback(show_selectable_folders_options) elif modify_option == lang.pack['Files']['Modify_Files']['Copy']: #Logger.info("Copying files") self.MODE = 'COPY' show_selectable_folders_options = { 'callback': self.get_directory, 'icon': "Icons/Files_Icons/File_Options/Next.png" } self.show_folders_callback(show_selectable_folders_options) def get_directory(self, directory, file_explorer_callback): #Logger.info("Moving selected files to directory " + str(directory)) if self.MODE == 'MOVE': self.move_files(directory, file_explorer_callback) elif self.MODE == 'COPY': self.copy_files(directory, file_explorer_callback) else: raise Exception("self.MODE is not set") #This function takes in filename and destination and outputs a filename with a _#.extension def name_iterator(self, filename, destination, file_type="folder"): #get the name and extension name, extension = os.path.splitext(filename) final_name = destination + "/" + name + extension #gotta check if the basename is available file_exists = True #initialize the value #different function based on filetype if file_type == "folder": file_exists = self.storage.folder_exists(final_name) else: file_exists = self.storage.file_exists(final_name) #if file doesn't exist then it will just return the basename counter = 0 while file_exists: counter += 1 final_name = destination + "/" + name + "_" + str( counter) + extension if file_type == "folder": file_exists = self.storage.folder_exists(final_name) else: file_exists = self.storage.file_exists(final_name) return final_name #This function is used for testing if two paths are the same file def same_file(self, filepath, name, destination): Logger.info(filepath) Logger.info(name) Logger.info(destination) if destination == '': final_destination = name else: final_destination = destination + "/" + name Logger.info(final_destination) if filepath == final_destination: return True return False ''' Earlier in the class the user selects the files that he wants to modify, then chooses the type of modification(Copy). Then the user selects the destination and then copy_files recieves a destination folder and a callback to call when it's done modifying the files. Copy_Files then iterates over a list of files, if the file exists at the destination folder already the User gets notified and asked to choose to either Replace, Keep Both Files, Or just skip the file. Then the Correct action takes place and the function moves onto the next file. Rinse Repeat, then call the callback when finished ''' def copy_files(self, destination, callback): #display Popup self.prog_popup.show() self.counter = 0 #progress self.max_count = len(self.selected_files) self.error_popup = False def skip(): self.counter += 1 prog = float(float(self.counter) / float(self.max_count) * 100.00) #percent progress self.modify_progress.update_progress(prog) self.modify_progress.file_exists = "[size=30]" + lang.pack[ 'Files']['Modify_Files']['Progress'] + str( self.counter) + " / " + str( self.max_count ) + lang.pack['Files']['Modify_Files']['Files'] #Logger.info( str(self.counter) + " " + str(self.max_count) + " " + str(prog)) self.waiting_for_input = False self.modify_progress.button_state = True Clock.schedule_once(copy_next_file, 0.2) #loop def replace(): self.modify_progress.button_state = True self.modify_progress.file_exists = "[size=30]" + lang.pack[ 'Files']['Modify_Files']['Progress'] + str( self.counter) + " / " + str( self.max_count ) + lang.pack['Files']['Modify_Files']['Files'] def replace_action(dt): try: #check to see if the file is the same file we are about to delete source_abs_path = self.storage.path_on_disk( self.file['path']) dest_abs_path = self.storage.path_on_disk( destination + "/" + self.file['name']) if os.path.samefile(source_abs_path, dest_abs_path): #Logger.info("File and destination are the same, Not doing anything\n" + str(source_abs_path) + "\n" + str(dest_abs_path)) pass else: if self.file['type'] == "folder": #Logger.info("Removing File") self.storage.remove_folder(destination + "/" + self.file['name'], recursive=True) #Logger.info("Copying File") self.storage.copy_folder( self.file['path'], destination + "/" + self.file['name']) else: #Logger.info("Removing File") self.storage.remove_file(destination + "/" + self.file['name']) #Logger.info("Copying File") self.storage.copy_file( self.file['path'], destination + "/" + self.file['name']) except Exception as e: Logger.info("!!!!!!!!!!!!!!! Error: " + str(e)) traceback.print_exc() error_out() finally: skip() #schedule the replace action so the screen can update accordingly Clock.schedule_once(replace_action, 0.2) def keep_both(): self.modify_progress.button_state = True self.modify_progress.file_exists = "[size=30]" + lang.pack[ 'Files']['Modify_Files']['Progress'] + str( self.counter) + " / " + str( self.max_count ) + lang.pack['Files']['Modify_Files']['Files'] #check if the file exists or not and ask user what they want to do def keep_action(dt): try: if self.file['type'] == "folder": final_name = self.name_iterator(self.file['name'], destination, file_type='folder') self.storage.copy_folder(self.file['path'], final_name) else: final_name = self.name_iterator(self.file['name'], destination, file_type='file') self.storage.copy_file(self.file['path'], final_name) except Exception as e: Logger.info("!!!!!!!!!!!!!!! Error: " + str(e)) traceback.print_exc() error_out() finally: skip() Clock.schedule_once(keep_action, 0.2) def file_exists(): self.modify_progress.file_exists = "[size=30][color=FF0000]" + lang.pack[ 'Files']['Modify_Files']['File_Exists'] self.modify_progress.replace = replace self.modify_progress.keep_both = keep_both self.modify_progress.skip = skip self.modify_progress.button_state = False self.waiting_for_input = True def error_out(): if not self.error_popup: self.error_popup = True ep = Error_Popup( lang.pack['Files']['File_Options']['Copy_Error']['Title'], lang.pack['Files']['File_Options']['Copy_Error']['Body']) ep.show() self.counter = self.max_count - 1 #Copy next ticker. This gets called until all files have been copied. It updates the progress bar. def copy_next_file(*args, **kwargs): try: if self.counter < self.max_count: file = self.selected_files[self.counter] self.file = file #Logger.info("Copying: " + str(file['name']) + " " + str(file['path']) + " to " + destination) self.modify_progress.update_file(str(file['name'])) #check if the file exists or not and ask user what they want to do if file['type'] == "folder": if not self.storage.folder_exists(destination + "/" + file['name']): self.storage.copy_folder( file['path'], destination + "/" + file['name']) else: file_exists() else: if not self.storage.file_exists(destination + "/" + file['name']): self.storage.copy_file( file['path'], destination + "/" + file['name']) else: file_exists() except Exception as e: Logger.info("!!!!!!!!!!!!!!! Error: " + str(e)) traceback.print_exc() error_out() #check if it's finished if self.counter == self.max_count: self.prog_popup.hide() #return file options to root options while self.return_to_previous_list(): pass callback() return False else: if not self.waiting_for_input: skip() Clock.schedule_once(copy_next_file, 0.2) ''' move_files does the same thing as copy_files, but it actually moves the original file. I know we are supposed to use the DRY method, but Copy files is all over the place and adding in another clause for checking the mode seemed to make the source code even more confusing than it already is. So I opted to just repeat copy files except change copy_folder/file to move_folder/file ''' def move_files(self, destination, callback): #display Popup self.prog_popup.show() self.counter = 0 #progress self.max_count = len(self.selected_files) self.error_popup = False def skip(): self.counter += 1 prog = float(float(self.counter) / float(self.max_count) * 100.00) #percent progress self.modify_progress.update_progress(prog) self.modify_progress.file_exists = "[size=30]" + lang.pack[ 'Files']['Modify_Files']['Progress'] + str( self.counter) + " / " + str( self.max_count ) + lang.pack['Files']['Modify_Files']['Files'] #Logger.info( str(self.counter) + " " + str(self.max_count) + " " + str(prog)) self.waiting_for_input = False self.modify_progress.button_state = True Clock.schedule_once(move_next_file, 0.2) #loop def replace(): self.modify_progress.button_state = True self.modify_progress.file_exists = "[size=30]" + lang.pack[ 'Files']['Modify_Files']['Progress'] + str( self.counter) + " / " + str( self.max_count ) + lang.pack['Files']['Modify_Files']['Files'] def replace_action(dt): try: #check to see if the file is the same file we are about to delete source_abs_path = self.storage.path_on_disk( self.file['path']) dest_abs_path = self.storage.path_on_disk( destination + "/" + self.file['name']) if os.path.samefile(source_abs_path, dest_abs_path): #Logger.info("File and destination are the same, Not doing anything\n" + str(source_abs_path) + "\n" + str(dest_abs_path)) pass else: if self.file['type'] == "folder": #Logger.info("Removing File") self.storage.remove_folder(destination + "/" + self.file['name'], recursive=True) #Logger.info("Moving File") self.storage.move_folder( self.file['path'], destination + "/" + self.file['name']) else: #Logger.info("Removing File") self.storage.remove_file(destination + "/" + self.file['name']) #Logger.info("Moving File") self.storage.move_file( self.file['path'], destination + "/" + self.file['name']) except Exception as e: Logger.info("!!!!!!!!!!!!!!! Error: " + str(e)) traceback.print_exc() error_out() finally: skip() #schedule the replace action so the screen can update accordingly Clock.schedule_once(replace_action, 0.2) def keep_both(): self.modify_progress.button_state = True self.modify_progress.file_exists = "[size=30]" + lang.pack[ 'Files']['Modify_Files']['Progress'] + str( self.counter) + " / " + str( self.max_count ) + lang.pack['Files']['Modify_Files']['Files'] #check if the file exists or not and ask user what they want to do def keep_action(dt): try: if self.file['type'] == "folder": same_folder = self.same_file(self.file['path'], self.file['name'], destination) final_name = self.name_iterator(self.file['name'], destination, file_type='folder') if not same_folder: self.storage.move_folder(self.file['path'], final_name) else: self.storage.copy_folder(self.file['path'], final_name) else: same_file = self.same_file(self.file['path'], self.file['name'], destination) final_name = self.name_iterator(self.file['name'], destination, file_type='file') if not same_file: self.storage.move_file(self.file['path'], final_name) else: self.storage.copy_file(self.file['path'], final_name) except Exception as e: Logger.info("!!!!!!!!!!!!!!! Error: " + str(e)) traceback.print_exc() error_out() finally: skip() Clock.schedule_once(keep_action, 0.2) def file_exists(): self.modify_progress.file_exists = "[size=30][color=FF0000]" + lang.pack[ 'Files']['Modify_Files']['File_Exists'] self.modify_progress.replace = replace self.modify_progress.keep_both = keep_both self.modify_progress.skip = skip self.modify_progress.button_state = False self.waiting_for_input = True def error_out(): if not self.error_popup: self.error_popup = True ep = Error_Popup( lang.pack['Files']['File_Options']['Move_Error']['Title'], lang.pack['Files']['File_Options']['Move_Error']['Body']) ep.show() self.counter = self.max_count - 1 #Copy next ticker. This gets called until all files have been copied. It updates the progress bar. def move_next_file(*args, **kwargs): try: if self.counter < self.max_count: file = self.selected_files[self.counter] self.file = file #Logger.info("Moving: " + str(file['name']) + " " + str(file['path']) + " to " + destination) self.modify_progress.update_file(str(file['name'])) #check if the file exists or not and ask user what they want to do if file['type'] == "folder": if not self.storage.folder_exists(destination + "/" + file['name']): self.storage.move_folder( file['path'], destination + "/" + file['name']) else: file_exists() else: if not self.storage.file_exists(destination + "/" + file['name']): self.storage.move_file( file['path'], destination + "/" + file['name']) else: file_exists() except Exception as e: Logger.info("!!!!!!!!!!!!!!! Error: " + str(e)) traceback.print_exc() error_out() #check if it's finished if self.counter == self.max_count: self.prog_popup.hide() #return file options to root options while self.return_to_previous_list(): pass callback() return False else: if not self.waiting_for_input: skip() Clock.schedule_once(move_next_file, 0.2) ''' delete_files will delete all selected files and show the progress to the screen. ''' def delete_files(self): #display Popup self.prog_popup.show() self.counter = 0 #progress self.max_count = len(self.selected_files) self.error_popup = False def error_out(): if not self.error_popup: self.error_popup = True ep = Error_Popup( lang.pack['Files']['File_Options']['Delete_Error'] ['Title'], lang.pack['Files']['File_Options']['Delete_Error']['Body']) ep.show() self.counter = self.max_count - 1 def skip(): self.counter += 1 prog = float(float(self.counter) / float(self.max_count) * 100.00) #percent progress self.modify_progress.update_progress(prog) self.modify_progress.file_exists = "[size=30]" + lang.pack[ 'Files']['Modify_Files']['Progress'] + str( self.counter) + " / " + str( self.max_count ) + lang.pack['Files']['Modify_Files']['Files'] #Logger.info( str(self.counter) + " " + str(self.max_count) + " " + str(prog)) self.waiting_for_input = False self.modify_progress.button_state = True Clock.schedule_once(delete_next_file, 0.2) #loop #delete ticker, this will delete all files until there are no more left def delete_next_file(*args, **kwargs): try: if self.counter < self.max_count: file = self.selected_files[self.counter] self.file = file #Logger.info("Deleting: " + str(file['name']) + " " + str(file['path'])) self.modify_progress.update_file(str(file['name'])) #delete the files if file['type'] == "folder": self.storage.remove_folder(file['path'], recursive=True) else: self.storage.remove_file(file['path']) except: Logger.info("!!!!!!!!!!!!!!! Error: " + str(e)) traceback.print_exc() error_out() #check if it's finished if self.counter == self.max_count: self.prog_popup.hide() #return file options to root options while self.return_to_previous_list(): pass return False else: if not self.waiting_for_input: skip() Clock.schedule_once(delete_next_file, 0.2) #This will change the permissions to the USB folder and try again. This # def change_permissions_and_try_create_folder(self, destination): # try: # import os # import subprocess # #chown the USB folder # USB_folder = os.path.expanduser('~/.octoprint/uploads/USB') # command = "sudo chown pi:pi " + str(USB_folder) # Logger.info("Executing command " + str(command)) # #wait for command to finish executing # subprocess.Popen(command, # stdout=subprocess.PIPE, # shell=True # ) # output, error = temp_p.communicate() # p_status = temp_p.wait() # Logger.info("Finished Executing! Attempting to add folder!") # self.storage.add_folder(destination, ignore_existing=True) # return True # except Exception as e: # Logger.info("Creating Permissions Failed! Erroring out!") # return False def create_new_folder(self, destination, file_callback): #get the current screen so we can travel back to it current_screen = roboprinter.screen_controls.get_screen_data() back_function = partial( roboprinter.screen_controls.populate_old_screen, screen=current_screen) self.error_popup = False def error_out(): if not self.error_popup: self.error_popup = True ep = Error_Popup( lang.pack['Files']['File_Options']['Folder_Error'] ['Title'], lang.pack['Files']['File_Options']['Folder_Error']['Body']) ep.show() def create_keyboard( default_text=lang.pack['Files']['Keyboard']['New_Folder']): #populate a screen with a keyboard as the object KeyboardInput_file_bb(keyboard_callback=get_keyboard_data, back_destination=current_screen, default_text=default_text) def get_keyboard_data(folder_name): def rename(): create_keyboard(default_text=folder_name) def make_new_folder_action(): try: self.storage.add_folder(destination + '/' + folder_name, ignore_existing=True) except Exception as e: Logger.info("!!!!!!!!!!!!!!!!!!!!! Error!") traceback.print_exc() error_out() #return file options to root options while self.return_to_previous_list(): pass #go back to file explorer file_callback() if self.storage.folder_exists(destination + '/' + folder_name): title = lang.pack['Files']['Keyboard']['Error']['Title'] body_text = lang.pack['Files']['Keyboard']['Error']['Body'] option1 = lang.pack['Files']['Keyboard']['Error']['Rename'] option2 = lang.pack['Files']['Keyboard']['Error']['Cancel'] modal_screen = Modal_Question_No_Title(body_text, option1, option2, rename, back_function) #make screen roboprinter.screen_controls.set_screen_content( modal_screen, title, back_function=back_function, option_function='no_option') else: make_new_folder_action() create_keyboard()