diff --git a/src/materia/models/__init__.py b/src/materia/models/__init__.py index 646bc3b..c72d7a7 100644 --- a/src/materia/models/__init__.py +++ b/src/materia/models/__init__.py @@ -16,6 +16,8 @@ from materia.models.database import ( from materia.models.user import User, UserCredentials, UserInfo +from materia.models.filesystem import FileSystem + from materia.models.repository import ( Repository, RepositoryInfo, diff --git a/src/materia/models/directory.py b/src/materia/models/directory.py index a6b8d18..21a510c 100644 --- a/src/materia/models/directory.py +++ b/src/materia/models/directory.py @@ -47,22 +47,19 @@ class Directory(Base): await session.flush() await session.refresh(self, attribute_names=["repository"]) - relative_path = await self.relative_path(session) + repository_path = await self.repository.path(session, config) directory_path = await self.path(session, config) - try: - directory_path.mkdir() - except OSError as e: - raise DirectoryError( - f"Failed to create directory at /{relative_path}:", - *e.args, - ) + new_directory = FileSystem(directory_path, repository_path) + await new_directory.make_directory() return self async def remove(self, session: SessionContext, config: Config): session.add(self) - await session.refresh(self, attribute_names=["directories", "files"]) + await session.refresh( + self, attribute_names=["repository", "directories", "files"] + ) if self.directories: for directory in self.directories: @@ -72,15 +69,11 @@ class Directory(Base): for file in self.files: file.remove(session, config) - relative_path = await self.relative_path(session) + repository_path = await self.repository.path(session, config) directory_path = await self.path(session, config) - try: - shutil.rmtree(str(directory_path)) - except OSError as e: - raise DirectoryError( - f"Failed to remove directory at /{relative_path}:", *e.args - ) + current_directory = FileSystem(directory_path, repository_path) + await current_directory.remove() await session.delete(self) await session.flush() @@ -153,38 +146,59 @@ class Directory(Base): async def copy( self, directory: Optional["Directory"], session: SessionContext, config: Config ) -> Self: - pass + session.add(self) + await session.refresh(self, attribute_names=["repository"]) + + repository_path = await self.repository.path(session, config) + directory_path = await self.path(session, config) + directory_path = ( + await directory.path(session, config) if directory else repository_path + ) + + current_directory = FileSystem(directory_path, repository_path) + new_directory = await current_directory.copy(directory_path) + + cloned = self.clone() + cloned.name = new_directory.name() + cloned.parent_id = directory.id if directory else None + session.add(cloned) + await session.flush() + + return self async def move( self, directory: Optional["Directory"], session: SessionContext, config: Config ) -> Self: - pass + session.add(self) + await session.refresh(self, attribute_names=["repository"]) + + repository_path = await self.repository.path(session, config) + directory_path = await self.path(session, config) + directory_path = ( + await directory.path(session, config) if directory else repository_path + ) + + current_directory = FileSystem(directory_path, repository_path) + moved_directory = await current_directory.move(directory_path) + + self.name = moved_directory.name() + self.parent_id = directory.id if directory else None + self.updated = time() + await session.flush() + + return self async def rename(self, name: str, session: SessionContext, config: Config) -> Self: session.add(self) + await session.refresh(self, attribute_names=["repository"]) + repository_path = await self.repository.path(session, config) directory_path = await self.path(session, config) - relative_path = await self.relative_path(session) - new_path = directory_path.with_name(name) - identity = 1 - while True: - if new_path == directory_path: - break - if not new_path.exists(): - break + current_directory = FileSystem(directory_path, repository_path) + renamed_directory = await current_directory.rename(name, force=True) - new_path = directory_path.with_name(f"{name}.{str(identity)}") - identity += 1 - - try: - await aiofiles.os.rename(directory_path, new_path) - except OSError as e: - raise DirectoryError( - f"Failed to rename directory at /{relative_path}", *e.args - ) - - self.name = new_path.name + self.name = renamed_directory.name() await session.flush() return self @@ -222,3 +236,4 @@ class DirectoryInfo(BaseModel): from materia.models.repository import Repository from materia.models.file import File +from materia.models.filesystem import FileSystem diff --git a/src/materia/models/file.py b/src/materia/models/file.py index a7b9c5f..574ca49 100644 --- a/src/materia/models/file.py +++ b/src/materia/models/file.py @@ -47,18 +47,12 @@ class File(Base): await session.flush() await session.refresh(self, attribute_names=["repository"]) - relative_path = await self.relative_path(session) file_path = await self.path(session, config) - size = None - try: - async with aiofiles.open(file_path, mode="wb") as file: - await file.write(data) - size = (await aiofiles.os.stat(file_path)).st_size - except OSError as e: - raise FileError(f"Failed to write file at /{relative_path}", *e.args) + new_file = FileSystem(file_path, await self.repository.path(session, config)) + await new_file.write_file(data) - self.size = size + self.size = await new_file.size() await session.flush() return self @@ -66,13 +60,10 @@ class File(Base): async def remove(self, session: SessionContext, config: Config): session.add(self) - relative_path = await self.relative_path(session) file_path = await self.path(session, config) - try: - await aiofiles.os.remove(file_path) - except OSError as e: - raise FileError(f"Failed to remove file at /{relative_path}:", *e.args) + new_file = FileSystem(file_path, await self.repository.path(session, config)) + await new_file.remove() await session.delete(self) await session.flush() @@ -149,15 +140,12 @@ class File(Base): directory_path = ( await directory.path(session, config) if directory else repository_path ) - new_path = File.generate_name(file_path, directory_path, self.name) - try: - await aioshutil.copy(file_path, new_path) - except OSError as e: - raise FileError("Failed to move file:", *e.args) + current_file = FileSystem(file_path, repository_path) + new_file = await current_file.copy(directory_path) cloned = self.clone() - cloned.name = new_path.name + cloned.name = new_file.name() cloned.parent_id = directory.id if directory else None session.add(cloned) await session.flush() @@ -175,51 +163,28 @@ class File(Base): directory_path = ( await directory.path(session, config) if directory else repository_path ) - new_path = File.generate_name(file_path, directory_path, self.name) - try: - await aioshutil.move(file_path, new_path) - except OSError as e: - raise FileError("Failed to move file:", *e.args) + current_file = FileSystem(file_path, repository_path) + moved_file = await current_file.move(directory_path) - self.name = new_path.name + self.name = moved_file.name() self.parent_id = directory.id if directory else None self.updated = time() await session.flush() return self - @staticmethod - def generate_name(old_file: Path, target_directory: Path, name: str) -> Path: - new_path = target_directory.joinpath(name) - identity = 1 - - while True: - if new_path == old_file: - break - if not new_path.exists(): - break - - new_path = target_directory.joinpath( - f"{name.removesuffix(new_path.suffix)}.{str(identity)}{new_path.suffix}" - ) - identity += 1 - - return new_path - async def rename(self, name: str, session: SessionContext, config: Config) -> Self: session.add(self) + await session.refresh(self, attribute_names=["repository"]) + repository_path = await self.repository.path(session, config) file_path = await self.path(session, config) - relative_path = await self.relative_path(session) - new_path = File.generate_name(file_path, file_path.parent, name) - try: - await aiofiles.os.rename(file_path, new_path) - except OSError as e: - raise FileError(f"Failed to rename file at /{relative_path}", *e.args) + current_file = FileSystem(file_path, repository_path) + renamed_file = await current_file.rename(name, force=True) - self.name = new_path.name + self.name = renamed_file.name() self.updated = time() await session.flush() return self @@ -263,3 +228,4 @@ class FileInfo(BaseModel): from materia.models.repository import Repository from materia.models.directory import Directory +from materia.models.filesystem import FileSystem diff --git a/src/materia/models/filesystem.py b/src/materia/models/filesystem.py index c5628a4..909dcce 100644 --- a/src/materia/models/filesystem.py +++ b/src/materia/models/filesystem.py @@ -44,6 +44,9 @@ class FileSystem: async def is_directory(self) -> bool: return await async_path.isdir(self.path) + def name(self) -> str: + return self.path.name + async def remove(self): try: if await self.is_file(): @@ -67,18 +70,18 @@ class FileSystem: return name while await async_path.exists(new_path): - if self.is_file(): - if with_counter := re.match(r"^(.+)(\.\d+)(\.\w+)$", new_path.name): + if await self.is_file(): + if with_counter := re.match(r"^(.+)\.(\d+)\.(\w+)$", new_path.name): new_name, _, extension = with_counter.groups() - elif with_extension := re.match(r"^(.+)(\.\w+)$", new_path.name): + elif with_extension := re.match(r"^(.+)\.(\w+)$", new_path.name): new_name, extension = with_extension.groups() new_path = target_directory.joinpath( "{}.{}.{}".format(new_name, count, extension) ) - if self.is_directory(): - if with_counter := re.match(r"^(.+)(\.\d+)$", new_path.name): + if await self.is_directory(): + if with_counter := re.match(r"^(.+)\.(\d+)$", new_path.name): new_name, _ = with_counter.groups() else: new_name = new_path.name @@ -106,12 +109,10 @@ class FileSystem: "Failed to move content to target destination: already exists" ) - await aioshutil.move( - self.path, - target_directory.joinpath( - await self.generate_name(target_directory, new_name) - ), + new_path = target_directory.joinpath( + await self.generate_name(target_directory, new_name) ) + await aioshutil.move(self.path, new_path) except Exception as e: raise FileSystemError( @@ -119,15 +120,17 @@ class FileSystem: *e.args, ) - async def rename(self, new_name: str, force: bool = False): - await self.move(self.path.parent, new_name=new_name, force=force) + return FileSystem(new_path, self.working_directory) + + async def rename(self, new_name: str, force: bool = False) -> Path: + return await self.move(self.path.parent, new_name=new_name, force=force) async def copy( self, target_directory: Path, new_name: Optional[str] = None, force: bool = False, - ): + ) -> Self: new_name = new_name if new_name else self.path.name try: @@ -155,6 +158,8 @@ class FileSystem: *e.args, ) + return FileSystem(new_path, self.working_directory) + async def make_directory(self): try: if await self.exists():